[Из песочницы] Класс Forecaster для метеостанции или Предсказатель погоды

#include "forecast.h"
#include "math.h"
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <Time.h>
#include <avr/pgmspace.h>
//////////////////////////////////////////////////////////////////////////
// По умолчанию в драйвере w5100 размер приемного буфера 2Кб (4 сокета по 2Кб)
// объем XML данных страницы с 3х часовым прогнозом составляет 15-18Кб
// Что бы минимизировать потерю информации необходимо увеличить размер приемного буфера
// до 4Кб (в идеале 1 сокет и 8Кб, но так не работает вообще).
//////////////////////////////////////////////////////////////////////////
// По этому вносим изменения в файле w5100.h
// MAX_SOCK_NUM 2
// SOCKETS = 2;
// SMASK = 0x0FFF; // Tx buffer MASK
// RMASK = 0x0FFF; // Rx buffer MASK
// SSIZE = 4096; // Max Tx buffer size
// RSIZE = 4096; // Max Rx buffer size
//////////////////////////////////////////////////////////////////////////
// В файле w5100.cpp
// TX_RX_MAX_BUF_SIZE 4096
// writeTMSR(0xAA);
// writeRMSR(0xAA);
//////////////////////////////////////////////////////////////////////////
// В файле Ethernet.h
// MAX_SOCK_NUM 2
//////////////////////////////////////////////////////////////////////////
// В файле Ethernet.cpp
// uint8_t EthernetClass::_state[MAX_SOCK_NUM] = { 0, 0 };
// uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = { 0 , 0 };
//////////////////////////////////////////////////////////////////////////
#include <Ethernet.h>
///////////////////////////////////////////////////////////////////////////////////////////////
// Особой необходимости использовать PROGMEM нет, но для разнообразия. В результате освободили 504 байта RAM
const char p_FCserver[]     PROGMEM  = "api.openweathermap.org";
const char p_APIID[]        PROGMEM  = "&APPID="; //Тут надо вписать свой API-ключ, но работает и без него
const char p_request3Hour[] PROGMEM  = "GET /data/2.5/forecast?q=Krasnoyarsk&mode=xml&units=metric";
const char p_request4Day[]  PROGMEM  = "GET /data/2.5/forecast/daily?q=Krasnoyarsk&mode=xml&units=metric&cnt=4";
const char p_requestToDay[] PROGMEM  = "GET /data/2.5/weather?q=Krasnoyarsk&mode=xml&units=metric";
const char p_ConnClose[]    PROGMEM  = "Connection: close";
const char p_HTTP[]         PROGMEM  = " HTTP/1.1";
PGM_P const string_table[]  PROGMEM  = {p_FCserver, p_APIID,p_request3Hour,p_request4Day,p_requestToDay,p_ConnClose,p_HTTP};
char words[80];
///////////////////////////////////////////////////////////////////////////////////////////////
#define MAX_STRING_LEN  100
char tagStr[MAX_STRING_LEN] = "";
char tmpStr[MAX_STRING_LEN] = "";
char endTag[3] = {'<', '/', '\0'};
char inChar;
// Конструктор
FORECAST::FORECAST()
{
        fDebug = false;
}
// В качестве инициализации передаем сссылку на EthernetClient, и часовой пояс
// часовой пояс нужен что бы скорректировать время в прогнозе, т.к. изночально дано время по гринвичу
void FORECAST::Init(EthernetClient& clnt, int timeZone)
{
        client = clnt;
        tZone = timeZone;
        tDelay = 1000;
}
// Получение 3х часового прогноза (2 дня с дискретностью 3 часа)
int FORECAST::GetForecast3Hour(FORECAST::_WeatherThreeHour *whp)
{
        int p1 = 0,p2 = 0;
        char TMP[20];
        String temp = "";
        float T;
  
  if (client.connected()) client.stop();
        if (client.connect(GetWord(0), 80))
        {
    temp  = GetWord(2);
    //temp += GetWord(1);
    temp += GetWord(6);
    //if (fDebug) Serial.println(temp);
                client.println(temp);
    client.print("Host: ");
    client.println(GetWord(0));
    client.println(GetWord(5));
    client.println();
    temp = "";
                // Ждем пока сервер ответит, если долго не отвечает - прервемся
                while (!client.available())
                {
                        p1++;
                        delay(50);
                        if ( p1 > tDelay )
                        {
                                client.stop();
                                return 0;
                        }
                }
                iPacket3H = 0;
                while (client.available())
                {
                        //-----------------------------------------------------------
                        inChar = client.read();
                        if (inChar == '<')   // начало строки
                        {
                                addChar(inChar, tmpStr);
                                tagFlag = true;
                        }
                        else if (inChar == '>')      // конец строки
                        {
                                addChar(inChar, tmpStr);
                                if (tagFlag)    // копируем временную строку в результирующую
                                {
                                  strncpy(tagStr, tmpStr, strlen(tmpStr)+1);
                                }
                                clearStr(tmpStr);
                                tagFlag = false;
                        }
                        else if (inChar != 10)
                        {
                                if (tagFlag)
                                {
                                        addChar(inChar, tmpStr);
                                        // Check for </XML> end tag, ignore it
                                        if ( tagFlag && strcmp(tmpStr, endTag) == 0 )
                                        {
                                                clearStr(tmpStr);
                                                tagFlag = false;
                                        }
                                }
                        }
                        // если конец строки - обработаем ее
                        if ((inChar == 10 ) || (inChar == '>'))
                        {
                                dataString = tagStr;
                                if( !strncmp(tagStr,"<time from",10))
                                { //<time from="2014-11-26T03:00:00" to="2014-11-26T06:00:00">
          //if (fDebug) Serial.println(tagStr);
                                        p1 =  dataString.indexOf('"',1);
                                        p1++;
                                        p2 =  dataString.indexOf('"',p1);
                                        temp = dataString.substring(p1,p2-3);
                                        temp.replace("T"," ");
                                        temp.toCharArray(TMP,temp.length()+1);
                                        (*whp)[iPacket3H].Data = _ConvertDateTime(TMP);
                                }
                                else if( !strncmp(tagStr,"<symbol",7))
                                { // <symbol number="600" name="light snow" var="13d"/>
                                        p1 =  dataString.indexOf("var");
                                        SubStrA(p1,dataString,temp);
                                        temp.toCharArray((*whp)[iPacket3H].Icon,temp.length()+1);
                                }
                                else if( !strncmp(tagStr,"<precipitation",14))
                                { // <precipitation unit="3h" value="0.125" type="rain">
          //if (fDebug) Serial.println(tagStr);
                                        int sign;
                                        clearStr(TMP);
                                        if (dataString.indexOf("rain") > 0) sign = 1;              // Дождь
                                        else if (dataString.indexOf("snow") > 0 || dataString.indexOf("show") > 0) sign = -1; // Снег //29.09.2015 - ошибка в тегах на сайте вместо snow стоит show 
                                        else    sign = 0;                                       // без осадков
          p1 =  dataString.indexOf("value");
                                        if (sign != 0)
                                        {
                                                SubStrA(p1,dataString,temp);
                                                temp.toCharArray(TMP,temp.length()+1);
                                                (*whp)[iPacket3H].RainVal = atof(TMP) * sign * 100;
                                        }
                                        else (*whp)[iPacket3H].RainVal = 0;
                                }
                                else if( !strncmp(tagStr,"<windDirection",14))
                                { //<windDirection deg="235" code="SW" name="Southwest"/>
                                        p1 =  dataString.indexOf("code");
                                        SubStrA(p1,dataString,temp);
                                        temp.toCharArray((*whp)[iPacket3H].WD,temp.length()+1);
                                }
                                else if( !strncmp(tagStr,"<windSpeed",10))
                                { //<windSpeed mps="4.62" name="Gentle Breeze"/>
                                        clearStr(TMP);
                                        SubStrA(1,dataString,temp);
                                        temp.toCharArray(TMP,temp.length()+1);
                                        T = atof(TMP);
                                        (*whp)[iPacket3H].WS = round(T);
                                }
                                else if( !strncmp(tagStr,"<temperature",12))
                                { //<temperature unit="celsius" value="-12.92" min="-17.91" max="-12.92"/>
                                        clearStr(TMP);
                                        p1 =  dataString.indexOf("value");
                                        SubStrA(p1,dataString,temp);
                                        temp.toCharArray(TMP,temp.length()+1);
                                        T = atof(TMP);
                                        (*whp)[iPacket3H].T = round(T);
                                        if (T < 0)
                                        p1 =  dataString.indexOf("min");
                                        else
                                        p1 =  dataString.indexOf("max");
                                        clearStr(TMP);
                                        SubStrA(p1,dataString,temp);
                                        temp.toCharArray(TMP,temp.length()+1);
                                        T = atof(TMP);
                                        (*whp)[iPacket3H].TT = round(T);
                                }
                                else if( !strncmp(tagStr,"<pressure",9))
                                { //<pressure unit="hPa" value="999.14"/>
                                        p1 =  dataString.indexOf("value");
                                        SubStrA(p1,dataString,temp);
                                        (*whp)[iPacket3H].P = temp.toInt();
                                        (*whp)[iPacket3H].P =((*whp)[iPacket3H].P * 0.75);    // - 17
                                }
                                else if( !strncmp(tagStr,"<humidity",9))
                                { //<humidity value="73" unit="%"/>
                                        SubStrA(1,dataString,temp);
                                        (*whp)[iPacket3H].H = temp.toInt();
                                }
                                else if( !strncmp(tagStr,"<clouds",7))
                                { // <clouds value="broken clouds" all="56" unit="%"/>
                                        p1 =  dataString.indexOf("all");
                                        SubStrA(p1,dataString,temp);
                                        (*whp)[iPacket3H].Cloud = temp.toInt();
                                        iPacket3H++;
                                }
                                clearStr(tmpStr);
                                clearStr(tagStr);
                                tagFlag = false;
                        }
                        //-----------------------------------------------------------
                        if (iPacket3H==16) {break;}
                }
                client.stop();
                return 1;
        }
        else {return 0;}
}
/////////////////////////////////////////////// Получение 4х дневного прогноза///////////////////////////////////////////////////////
/*
int FORECAST::GetForecastDays(FORECAST::_WeatherDay* wdp)
{
        int iPack=0,p=0;
        char TMP[20];
        String temp;
        float T;
  if (client.connected()) client.stop();
        if (client.connect(GetWord(0), 80))
        {
    temp =  GetWord(3);
    //temp += GetWord(1);
    temp += GetWord(6);
    if (fDebug) Serial.println(temp);
                client.println(temp);
    client.print("Host: ");
    client.println(GetWord(0));
    client.println(GetWord(5));
    client.println();
    temp = "";
                while (!client.available())
                {
                        p++;
                        delay(50);
                        if ( p > tDelay )
                        {
                                client.stop();
                                return 0;
                        }
                }
                while (client.available())
                {
                        inChar = client.read();
                        if (inChar == '<')   // начало строки
                        {
                                addChar(inChar, tmpStr);
                                tagFlag = true;
                        }
                        else if (inChar == '>')      // конец строки
                        {
                                addChar(inChar, tmpStr);

                                if (tagFlag)    // копируем временную строку в результирующую
                                {
                                  strncpy(tagStr, tmpStr, strlen(tmpStr)+1);
                                }
                                clearStr(tmpStr);
                                tagFlag = false;
                        }
                        else if (inChar != 10)
                        {
                                if (tagFlag)
                                {
                                        addChar(inChar, tmpStr);
                                        // Check for </XML> end tag, ignore it
                                        if ( tagFlag && strcmp(tmpStr, endTag) == 0 )
                                        {
                                                clearStr(tmpStr);
                                                tagFlag = false;
                                        }
                                }
                        }
                        // если конец строки - обработаем ее
                        if ((inChar == 10 )  || (inChar == '>'))
                        {
                                dataString = tagStr;

                                if( !strncmp(tagStr,"<time day",9))
                                { //<time day="2014-10-09">
                                        SubStrA(1,dataString,temp);
                                        //temp.toCharArray((*wdp)[iPack].Data,temp.length()+1);
                                        temp.toCharArray(TMP,temp.length()+1);
                                        (*wdp)[iPack].Data = _ConvertDate(TMP);
         //if (fDebug) Serial.println(temp);
                                }
                                else if( !strncmp(tagStr,"<symbol",7))
                                { // <symbol number="600" name="light snow" var="13d"/>
                                        p =  dataString.indexOf("var");
                                        SubStrA(p,dataString,temp);
                                        temp.toCharArray((*wdp)[iPack].Icon,temp.length()+1);
                                }
                                else if( !strncmp(tagStr,"<precipitation",14))
                                { // <precipitation value="1.25" type="snow"/>
                                        int sign;
                                        clearStr(TMP);
                                        if (dataString.indexOf("rain") > 0) sign = 1;              // Дождь
                                        else if (dataString.indexOf("snow") > 0 || dataString.indexOf("show") > 0) sign = -1; // Снег
                                        else    sign = 0;                                       // без осадков
                                        if (sign != 0)
                                        {
                                                p=1;
                                                SubStrA(p,dataString,temp);
                                                temp.toCharArray(TMP,temp.length()+1);
                                                (*wdp)[iPack].RainVal = atof(TMP) * sign * 100;
                                        }
                                        else (*wdp)[iPack].RainVal = 0;
                                }
                                else if( !strncmp(tagStr,"<windDirection",14))
                                { //<windDirection deg="235" code="SW" name="Southwest"/>
                                        p =  dataString.indexOf("code");
                                        SubStrA(p,dataString,temp);
                                        temp.toCharArray((*wdp)[iPack].WD,temp.length()+1);
                                }
                                else if( !strncmp(tagStr,"<windSpeed",10))
                                { //<windSpeed mps="4.62" name="Gentle Breeze"/>
                                        clearStr(TMP);
                                        SubStrA(1,dataString,temp);
                                        temp.toCharArray(TMP,temp.length()+1);
                                        T = atof(TMP);
                                        (*wdp)[iPack].WS = round(T);
                                }
                                else if( !strncmp(tagStr,"<temperature",12))
                                { //<temperature day="4.48" min="-1.12" max="4.48" night="-0.94" eve="-1.12" morn="2.26"/>
                                        clearStr(TMP);
                                        SubStrA(1,dataString,temp);
                                        temp.toCharArray(TMP,temp.length()+1);
                                        T = atof(TMP);
                                        (*wdp)[iPack].TD = round(T);
                                        clearStr(TMP);
                                        p =  dataString.indexOf("night");
                                        SubStrA(p,dataString,temp);
                                        temp.toCharArray(TMP,temp.length()+1);
                                        T = atof(TMP);
                                        (*wdp)[iPack].TN = round(T);
                                }
                                else if( !strncmp(tagStr,"<pressure",9))
                                { //<pressure unit="hPa" value="999.14"/>
                                        p =  dataString.indexOf("value");
                                        SubStrA(p,dataString,temp);
                                        (*wdp)[iPack].P = temp.toInt();
                                        (*wdp)[iPack].P =((*wdp)[iPack].P * 0.75 - 17);
                                }
                                else if( !strncmp(tagStr,"<humidity",9))
                                { //<humidity value="73" unit="%"/>
                                        SubStrA(1,dataString,temp);
                                        (*wdp)[iPack].H = temp.toInt();
                                }
                                else if( !strncmp(tagStr,"<clouds",7))
                                { // <clouds value="broken clouds" all="56" unit="%"/>
                                        p =  dataString.indexOf("all");
                                        SubStrA(p,dataString,temp);
                                        (*wdp)[iPack].Cloud = temp.toInt();
                                        iPack++;
                                }
                                clearStr(tmpStr);
                                clearStr(tagStr);
                                tagFlag = false;
                        }
                }
                // останавливаем клиент
                client.stop();
                return 1;
        }
        else {return 0;}
}
*/
///////////////////////////// Получение текущей погоды //////////////////////////////////////////////////////////////
/*
int FORECAST::GetForecast(FORECAST::_WeatherPacket& whr)
{
        int p1 = 0;
        char TMP[20];
        String temp = "";
        float T;
        int step = 0;
  if (client.connected()) client.stop();
        if (client.connect(GetWord(0), 80))
        {
    temp = GetWord(4);
    //temp += GetWord(1);
    temp += GetWord(6);
    if (fDebug) Serial.println(temp);
    client.println(temp);
    client.print("Host: ");
    client.println(GetWord(0));
    client.println(GetWord(5));
    client.println();
    temp = "";
                while (!client.available())             // Ждем пока поступит ответ
                {
                        p1++;
                        delay(50);
                        if ( p1 > tDelay )                           // если ожидание затянулось - прервем силком
                        {
                                client.stop();
                                return 0;
                        }
                }
                while (client.available())      //Ответ получен - вычитываем информацию
                {
                        // Read a char
                        inChar = client.read();
                        //if (fDebug) Serial.print(inChar);
                        if (inChar == '<')
                        {
                                addChar(inChar, tmpStr);
                                tagFlag = true;

                        }
                        else if (inChar == '>')
                        {
                                addChar(inChar, tmpStr);
                                if (tagFlag)
                                {
                                        strncpy(tagStr, tmpStr, strlen(tmpStr)+1);
                                }
                                clearStr(tmpStr);
                                tagFlag = false;
                                dataFlag = true;
                        }
                        else if (inChar != 10)
                        {
                                if (tagFlag)
                                {
                                        addChar(inChar, tmpStr);
                                        // Check for </XML> end tag, ignore it
                                        if ( tagFlag && strcmp(tmpStr, endTag) == 0 )
                                        {
                                                clearStr(tmpStr);
                                                tagFlag = false;
                                                dataFlag = false;
                                        }
                                }
                        }
                        // If a LF, process the line
                        if ((inChar == 10 ) || (inChar == '>'))
                        {
                                dataString = tagStr;
                                if( !strncmp(tagStr,"<sun",4))
                                { // <sun rise="2014-10-14T00:18:33" set="2014-10-14T10:51:01"/>
                                        SubStrA(1,dataString,temp);
                                        temp.replace("T"," ");
                                        temp.toCharArray(TMP,temp.length()+1);
                                        if (fDebug) {Serial.print("SunRise_w_");Serial.println(TMP);}
                                        SunRise = _ConvertDateTime(TMP);
                                        p1 =  dataString.indexOf("set");
                                        p1++;
                                        SubStrA(p1,dataString,temp);
                                        temp.replace("T"," ");
                                        temp.toCharArray(TMP,temp.length()+1);
                                        SunSet = _ConvertDateTime(TMP);
                                        step++;
                                }

                                else if( !strncmp(tagStr,"<temperature",12))
                                { //<temperature value="-9.86" min="-16" max="-6.3" unit="celsius"/>
                                        clearStr(TMP);
                                        SubStrA(1,dataString,temp);
                                        temp.toCharArray(TMP,temp.length()+1);
                                        T = atof(TMP);
                                        whr.T = round(T);
                                        step++;
          if (fDebug) Serial.println(tagStr);
                                }
                                else if( !strncmp(tagStr,"<humidity",9))
                                { //<humidity value="65" unit="%"/>
                                        SubStrA(1,dataString,temp);
                                        whr.H = temp.toInt();
                                        step++;
                                }
                                else if( !strncmp(tagStr,"<pressure",9)  )
                                { //<pressure value="1027" unit="hPa"/>
                                        SubStrA(1,dataString,temp);
                                        //      whr.P = round(temp.toInt() * 0.75 - 17);
                                        whr.P = temp.toInt() ;
                                        step++;
          if (fDebug) Serial.println(tagStr);
                                }
                                else if( !strncmp(tagStr,"<speed",6) )
                                { //<speed value="1.51" name=""/>
                                        clearStr(TMP);
                                        SubStrA(1,dataString,temp);
                                        temp.toCharArray(TMP,temp.length()+1);
                                        T = atof(TMP);
                                        whr.WS = round(T);
                                        step++;
          if (fDebug) Serial.println(tagStr);
                                }
                                else if( !strncmp(tagStr,"<direction",10)  )
                                { //<direction value="160.501" code="SSE" name="South-southeast"/>
                                        p1 =  dataString.indexOf("code");
                                        SubStrB(p1,dataString,temp);
                                        temp.toCharArray(whr.WD,temp.length()+1);
                                        step++;
          if (fDebug) Serial.println(tagStr);
                                }
                                else if( !strncmp(tagStr,"<weather",8)  )
                                { //<weather number="803" value="broken clouds" icon="04d"/>
                                        p1 =  dataString.indexOf("icon");
                                        SubStrA(p1,dataString,temp);
                                        temp.toCharArray(whr.Icon,temp.length()+1);
                                        step++;
          if (fDebug) {Serial.println(tagStr);Serial.println("-------------------");}
                                }
                                clearStr(tmpStr);
                                clearStr(tagStr);
                                tagFlag = false;
                                dataFlag = false;
                        }
                }
                // останавливаем клиент
                client.stop();
                if (step > 5) return 1; else return 0;
        }
        else {return 0;}
}
*/
//////////////////// Вспомогательные функции ////////////////////////////////////////////////////
void FORECAST::SubStrA(int Num,String& source, String& str)
{
        int p1 =  source.indexOf('"',Num);
        p1++;
        int p2 =  source.indexOf('"',p1);
        str = source.substring(p1,p2);
}
// переодически в прогнозе погоды направление ветра дают с ошибкой
void FORECAST::SubStrB(int Num,String& source, String& str)
{
        int p1 =  source.indexOf('"',Num);
        p1++;
        int p2 =  source.indexOf('"',p1+1);
        if (p2 - p1 < 1)
          str = "WNW";
        else
          str = source.substring(p1,p2);
}
//Function to add a char to a string and check its length
void FORECAST::addChar(char ch, char* str)
{
        char const  *tagMsg  = "!=!";
        if (strlen(str) > MAX_STRING_LEN - 2) {
                if (tagFlag)
                {
                        clearStr(tagStr);
                        strcpy(tagStr,tagMsg);
                }
                // Clear the temp buffer and flags to stop current processing
                clearStr(tmpStr);
                tagFlag = false;
        }
        else
        {
                // Add char to string
                str[strlen(str)] = ch;
        }
}
// Function to clear a string
void FORECAST::clearStr(char* str) {
        int len = strlen(str);
        for (int c = 0; c < len; c++) {
                str[c] = 0;
        }
}

time_t FORECAST::_ConvertDate(char _Data[11])
{
        int Y, M, D;
        TimeElements te;
        sscanf ( _Data,"%i-%d-%d", &Y, &M, &D);
        te.Year = Y -1900; te.Month = M; te.Day = D;
        te.Hour=0; te.Minute=0; te.Second=0;
        return makeTime(te);
}

time_t FORECAST::_ConvertDateTime(char _DateTime[17])
{
        int Y, M, D, hh, mm;
        TimeElements te;
        // Парсим строку (заполняем структуру датой из строки)
        sscanf ( _DateTime,"%i-%d-%d %d:%d", &Y, &M, &D, &hh, &mm );
        //  перводим время с учетом TimeZone
        hh = hh + tZone;
        if (hh > 24)
        {
                hh = hh - 24;
                if ( (M == 2 && D == 28 && Y%4 != 0)|| (M == 2 && D == 29 && Y%4 == 0)
                || ((M == 1 || M == 3 || M == 5 || M == 7 || M == 8 || M == 10 || M == 12) && D == 31)
                || ((M == 4 || M == 6 || M == 9 || M == 11) && D == 30) )
                {
                        M++;
                        D = 1;
                        if (M==13) {M=1;Y++;}
                }
                else  D++;
        }

        te.Year = Y -1900; te.Month = M; te.Day = D;
        te.Hour = hh; te.Minute = mm; te.Second=0;

        return makeTime(te);
}
// извлечение строки из flash - памяти по №строки
char* FORECAST::GetWord(uint8_t numWord)
{
    strcpy_P(words, (PGM_P)pgm_read_word(&(string_table[numWord])));
  return words;
}


© Geektimes