WiFi를 통한 아두이노 활용(17) : 아이폰 연동 #1

2017. 8. 25. 10:59

Arduino/Wireless

Arduino로 iphone에 데이터 전송하기 #1

이번 글에서는, 아두이노와 아이폰 사이의 간단한 데이터 교환을 연습해보겠습니다.

이제까지의 글들을 통해, 아두이노로 간단한 웹서버를 구현하였습니다. 그래서 아이폰의 웹 브라우저를 통해 웹페이지에 접근하면 데이터 교환이 가능하지만, 이번에는 웹이 아닌 앱을 통해 동일한 기능을 구현하도록 하겠습니다.

communication with XML

이전에 만든 openweathermap.org 날씨 데이터를 제공하는 웹서버 예제에 의해 이미 웹 서비스가 가능하므로, 아이폰 쪽에서 이 웹페이지의 HTML을 Parsing해서 원하는 데이터를 추출할 수도 있습니다. 하지만, XML을 이용하면 이 보다 좀더 쉽게 처리가 가능합니다. HTML과 달리 XML은 원하는대로 태그를 정의하여 사용할 수 있습니다. 이 점 때문에 데이터를 전송하는 코드가 더 단순해집니다. 게다가, ios에서 XML을 처리하는 방식도 비교적 쉽게 접근할 수 있기 때문에 이번 예제에선 XML을 사용하여 데이터를 전송 받도록 하겠습니다.

예제는 새로 작성하지 않고 이전에 작성한 예제를 수정하여 사용하겠습니다.

WiFi를 통한 아두이노 활용(13) : 날씨 정보 제공 #3

위 링크에서 작성한 예제를 바탕으로 하여 작성할 것이며, 전체 소스는 아래와 같습니다. openweathermap.org에서 날씨 정보를 받아 웹페이지로 클라이언트에게 제공하는 웹서버 예제입니다.

#include <SPI.h>
#include <WiFi101.h>
char ssid[] = "TURTLE";
char pass[] = "yesican1";
String cityID = "1835847"; // 기본값은 서울
String appID = "7b18dfe35b4273bbe63654d75573cacf"; // 각 계정의 APP ID
String selected1 = "";
String selected2 = "";
String selected3 = "";
String selected4 = "";
String selected5 = "";
String selected6 = "";
String selected7 = "";
bool tagInside = false;  // 태그 안쪽인지 바깥쪽인지 구별하는 변수
bool flagStartTag = false; // 스타트 태그인지 구별하는 변수
String currentTag = ""; // 현재 태그를 저장하기 위한 변수, 공백으로 초기화함
String currentData = ""; // 태그 사이의 컨텐츠를 저장하는 변수
String startTag = ""; // 현재 elements의 start tag 저장
String endTag = "";   // 현재 elements의 end tag 저장
String country = "";
String cityName = "";
String coordLon = "";
String coordLat = "";
String sunRise = "";
String sunSet = "";
String tempValue = "";
String humidValue = "";
String pressValue = "";
int status = WL_IDLE_STATUS;
char server[] = "api.openweathermap.org";
IPAddress ip;
WiFiServer Webserver(80); // Server 서비스를 위한 클래스 인스턴스
WiFiClient client;
void setup() {
  WiFi.setPins(8,7,4,2); 
  Serial.begin(9600);
  delay(1000);
  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue:
    while (true);
  }
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
    // wait 2 seconds for connection:
    delay(2000);
  }
  Serial.println("Connected to wifi");
  OpenWeatherMap();
  Webserver.begin();
  ip = WiFi.localIP();
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
}
void loop() {
  WiFiClient Webclient = Webserver.available();
  if (Webclient) {
    Serial.println("new client");
    bool newLine = false;
    String currentLine = "";
    while (Webclient.connected()) {
      if (Webclient.available()) {
        char c = Webclient.read();
        Serial.write(c);
        if (c == '\n') {
          if (newLine) {
            Serial.println("Client Request Ended!");
            Serial.println("Weather Data Request!");
            OpenWeatherMap(); 
            Serial.println("Web page transfer Start!");
            Webclient.println("HTTP/1.1 200 OK");
            Webclient.println("Content-type:text/html");
            Webclient.println("Connection: close");
            Webclient.println();
            Webclient.println("<!DOCTYPE html>");
            Webclient.println("<html>");
            Webclient.println("<head>");
            Webclient.println("<meta charset=\"UTF-8\">");
            Webclient.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">");
            Webclient.println("<title>openweathermap Weather Info</title>");
            Webclient.println("</head>");
            Webclient.println("<body>");
            Webclient.println("<h1>Weather Info of City</h1>");
            Webclient.println("<form>");
            Webclient.println("<select name=\"cityname\">");
            Webclient.println("<option value=\"1835847\" " + selected1 + ">서울특별시</option>");
            Webclient.println("<option value=\"1838519\" " + selected2 + ">부산광역시</option>");
            Webclient.println("<option value=\"1835327\" " + selected3 + ">대구광역시</option>");
            Webclient.println("<option value=\"1843561\" " + selected4 + ">인천광역시</option>");
            Webclient.println("<option value=\"1841808\" " + selected5 + ">광주광역시</option>");
            Webclient.println("<option value=\"1835224\" " + selected6 + ">대전광역시</option>");
            Webclient.println("<option value=\"1833742\" " + selected7 + ">울산광역시</option>");
            Webclient.println("</select>");
            Webclient.println("<input type=\"submit\">");
            Webclient.println("</form>");
            Webclient.println("<p>Country : " + country + "</p>");
            Webclient.println("<p>City Name : " + cityName + "</p>");
            Webclient.println("<p>Longitude : " + coordLon + "</p>");
            Webclient.println("<p>Latitude : " + coordLat + "</p>");
            Webclient.println("<p>Sun Rise : " + sunRise + "</p>");
            Webclient.println("<p>Sun Set : " + sunSet + "</p>");
            Webclient.println("<p>Temperature : " + tempValue + "</p>");
            Webclient.println("<p>Humidity : " + humidValue + "</p>");
            Webclient.println("<p>Pressure : " + pressValue + "</p>");
            Webclient.println("</body>");
            Webclient.println("</html>");
            break;
          } else {
            newLine = true; 
            if (currentLine.indexOf("HTTP/1.1") != -1) {
              requestProcess(currentLine);
            }
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c;
          newLine = false;
        }
      }
    } 
    Webclient.stop();
    Serial.println("client disonnected");
  }
}
void requestProcess(String requestLine) {
  int citynameIndex = requestLine.indexOf("cityname");
  if (citynameIndex != -1) {
    cityID = requestLine.substring(citynameIndex + 9, citynameIndex + 16);
  }
  selected1 = "";
  selected2 = "";
  selected3 = "";
  selected4 = "";
  selected5 = "";
  selected6 = "";
  selected7 = "";
  if (cityID == "1835847") {
    selected1 = "selected";
  } else if (cityID == "1838519") {
    selected2 = "selected";
  } else if (cityID == "1835327") {
    selected3 = "selected";
  } else if (cityID == "1843561") {
    selected4 = "selected";
  } else if (cityID == "1841808") {
    selected5 = "selected";
  } else if (cityID == "1835224") {
    selected6 = "selected";
  } else if (cityID == "1833742") {
    selected7 = "selected";
  }
}
void OpenWeatherMap() { 
  Serial.println("\nStarting connection to server..."); 
  if (client.connect(server, 80)) {
    Serial.println("connected to server");
    String request = "GET /data/2.5/weather?id=";
    request += cityID;
    request += "&APPID=";
    request += appID;
    request += "&units=metric&mode=xml HTTP/1.1"; 
    client.println(request);
    client.println("Host: api.openweathermap.org");
    client.println("Connection: close");
    client.println();
  } 
  while (client.connected()) {
    while (client.available()) {
      char c = client.read(); 
      if (c == '<') {
        tagInside = true;
      } 
      if (tagInside) {
        currentTag += c;
      } else if (flagStartTag) {
        currentData += c;
      } 
      if (c == '>') {
        tagInside = false;
        if (currentTag.startsWith("</")) {
          flagStartTag = false;
          endTag = currentTag; 
          if (startTag.indexOf("country") != -1) {
            if (endTag.indexOf("country") != -1) {
              Serial.print("Country : ");
              Serial.println(currentData);
              country = currentData;
            }
          }
          currentData = "";
        } else {
          flagStartTag = true;
          startTag = currentTag;
          startTagProcessing();
        }
        currentTag = "";
      }
    }
  }
  Serial.println();
  Serial.println("disconnecting from server.");
  client.stop();
}
void startTagProcessing() {
  if (startTag.startsWith("<city")) {
    int attribName = startTag.indexOf("name=");
    if (attribName != -1) {
      cityName = startTag.substring(attribName + 6);
      int quote = cityName.indexOf("\"");
      cityName = cityName.substring(0, quote);
      Serial.println("City : " + cityName);
    }
  } else if (startTag.startsWith("<coord")) {
    int attribLon = startTag.indexOf("lon=");
    if (attribLon != -1) {
      coordLon = startTag.substring(attribLon + 5);
      int quote = coordLon.indexOf("\"");
      coordLon = coordLon.substring(0, quote);
      Serial.println("Longitude : " + coordLon);
    }
    int attribLat = startTag.indexOf("lat=");
    if (attribLat != -1) {
      coordLat = startTag.substring(attribLat + 5);
      int quote = coordLat.indexOf("\"");
      coordLat = coordLat.substring(0, quote);
      Serial.println("Latitude : " + coordLat);
    }
  } else if (startTag.startsWith("<sun")) {
    int attribTime = startTag.indexOf("rise=");
    if (attribTime != -1) {
      String riseHour = startTag.substring(attribTime + 17, attribTime + 19);
      String riseMin = startTag.substring(attribTime + 20, attribTime + 22);
      int tempValue = riseHour.toInt();
      tempValue -= 15;
      riseHour = String(tempValue);
      Serial.println("Sun Rise : " + riseHour + ":" + riseMin);
      sunRise = riseHour + ":" + riseMin;
    }
    attribTime = startTag.indexOf("set=");
    if (attribTime != -1) {
      String setHour = startTag.substring(attribTime + 16, attribTime + 18);
      String setMin = startTag.substring(attribTime + 19, attribTime + 21);
      int tempValue = setHour.toInt();
      tempValue += 9;
      setHour = String(tempValue);
      Serial.println("Sun Set : " + setHour + ":" + setMin);
      sunSet = setHour + ":" + setMin;
    }
  } else if (startTag.startsWith("<temperature")) {
    int attribValue = startTag.indexOf("value=");
    if (attribValue != -1) {
      tempValue = startTag.substring(attribValue + 7);
      int quote = tempValue.indexOf("\"");
      tempValue = tempValue.substring(0, quote);
      Serial.println("Temperature : " + tempValue);
    }
  } else if (startTag.startsWith("<humidity")) {
    int attribValue = startTag.indexOf("value=");
    if (attribValue != -1) {
      humidValue = startTag.substring(attribValue + 7);
      int quote = humidValue.indexOf("\"");
      humidValue = humidValue.substring(0, quote);
      Serial.println("Humidity : " + humidValue + "%");
    }
  }else if (startTag.startsWith("<pressure")) { 
    int attribValue = startTag.indexOf("value=");
    if (attribValue != -1) {
      pressValue = startTag.substring(attribValue + 7);
      int quote = pressValue.indexOf("\"");
      pressValue = pressValue.substring(0, quote);
      Serial.println("Pressure : " + pressValue + " hPa");
    }
  }
}

위 예제에서 웹페이지 즉 HTML을 출력하는 부분만 변경해주면 XML 전송은 가능합니다. (소스에 있는 API key는 가짜입니다. 자신의 계정에 맞게 고쳐야 합니다.)

XML에 대해선 OpenWeatherMap.org에서 날씨데이터를 가져오는 부분에서 이미 다루었고 위 예제에도 받아온 날씨 데이터의 파싱처리를 위해 구현되어 있습니다.

XML 구조 및 root tag

XML Element : <start tag>Contents</end tag>

XML의 기본 단위는 Element 입니다. Element는 위와 같은 구조를 가집니다. HTML과 비슷하지만, 태그는 원하는 대로 정의가 가능하기 때문에, 전송하려는 데이터의 이름과 값을 태그와 컨텐츠로 묶어서 하나의 엘리먼츠로 만들어 보냅니다.

단, 저 XML 엘리먼트 구조를 꼭 지키셔야 합니다. XML 엘리먼트는 시작 태그와 종료 태그, 그리고 그 사이의 컨텐츠로 구성되어 집니다. 컨텐츠는 단순한 문자열일 수도 있고, 하나 이상의 또 다른 XML 엘리먼트일 수도 있습니다. HTML은 <br> 처럼 짝을 이루지 않는 태그도 있지만, XML은 꼭 시작 태그와 종료 태그의 짝을 맞춰 주도록 합니다. 따라서, XML 문서는 start tag로 시작해서 end tag로 끝나도록 구성해야 합니다. HTML의 <html>, </html> 태그처럼, XML도 시작 태그로 시작해서 종료태그로 끝나는 하나의 커다란 엘리먼트로 구성해야 합니다. 문서의 시작과 끝을 알리는 이러한 태그를 ROOT tag라고 합니다.

<온도>27.5</온도>
<습도>60</습도>

예를 들어, 온도와 습도를 전송한다면 위와 같이 한 항목당 한 개의 엘리먼트로 구성하면 됩니다. 전송하려는 정보가 10개라면 10개의 엘리먼트만 보내면 되기 때문에 코딩이 무척 단순해집니다. 하지만, 이대로 전송하면 문서 규칙에 위배됩니다. XML Parsing시에 error를 보게 됩니다.

<날씨>
  <온도>27.5</온도>
  <습도>60</습도>
</날씨>

이렇게 root tag 즉, start tag, end tag를 불여 하나의 elements로 만들어 전송해야 합니다.

<날씨>
  <온도 단위="섭씨">27.5</온도>
  <습도 단위="%">60</습도>
</날씨>

위와 같이 attribute를 설정하여 추가적인 데이터를 전송할 수도 있습니다. 하지만, 이번 글에서는 Xcode에서 코딩하는 부분까지 다루어야 하기 때문에 되도록 프로그램을 간단하게 만들기 위해 attribute는 구성하지 않겠습니다.

예제 소스 변경

Webclient.println("<!DOCTYPE html>");
Webclient.println("<html>");
Webclient.println("<head>");
Webclient.println("<meta charset=\"UTF-8\">");
Webclient.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">");
Webclient.println("<title>openweathermap Weather Info</title>");
Webclient.println("</head>");
Webclient.println("<body>");
Webclient.println("<h1>Weather Info of City</h1>");
Webclient.println("<form>");
Webclient.println("<select name=\"cityname\">");
Webclient.println("<option value=\"1835847\" " + selected1 + ">서울특별시</option>");
Webclient.println("<option value=\"1838519\" " + selected2 + ">부산광역시</option>");
Webclient.println("<option value=\"1835327\" " + selected3 + ">대구광역시</option>");
Webclient.println("<option value=\"1843561\" " + selected4 + ">인천광역시</option>");
Webclient.println("<option value=\"1841808\" " + selected5 + ">광주광역시</option>");
Webclient.println("<option value=\"1835224\" " + selected6 + ">대전광역시</option>");
Webclient.println("<option value=\"1833742\" " + selected7 + ">울산광역시</option>");
Webclient.println("</select>");
Webclient.println("<input type=\"submit\">");
Webclient.println("</form>");
Webclient.println("<p>Country : " + country + "</p>");
Webclient.println("<p>City Name : " + cityName + "</p>");
Webclient.println("<p>Longitude : " + coordLon + "</p>");
Webclient.println("<p>Latitude : " + coordLat + "</p>");
Webclient.println("<p>Sun Rise : " + sunRise + "</p>");
Webclient.println("<p>Sun Set : " + sunSet + "</p>");
Webclient.println("<p>Temperature : " + tempValue + "</p>");
Webclient.println("<p>Humidity : " + humidValue + "</p>");
Webclient.println("<p>Pressure : " + pressValue + "</p>");
Webclient.println("</body>");
Webclient.println("</html>");

웹페이지를 출력하는 부분만 가져왔습니다. 우선, 처음 아홉 줄까지의 <head>부분 등은 필요없으므로 삭제합니다. 기존 웹서버 예제에서 HTTP 전송 부분은 그대로 두고, 출력만 XML 태그로 구성하여 보내주면 아이폰 쪽에서 알아서 잘 처리해 줍니다.

Webclient.println("<form>");
Webclient.println("<select name=\"cityname\">");
Webclient.println("<option value=\"1835847\" " + selected1 + ">서울특별시</option>");
Webclient.println("<option value=\"1838519\" " + selected2 + ">부산광역시</option>");
Webclient.println("<option value=\"1835327\" " + selected3 + ">대구광역시</option>");
Webclient.println("<option value=\"1843561\" " + selected4 + ">인천광역시</option>");
Webclient.println("<option value=\"1841808\" " + selected5 + ">광주광역시</option>");
Webclient.println("<option value=\"1835224\" " + selected6 + ">대전광역시</option>");
Webclient.println("<option value=\"1833742\" " + selected7 + ">울산광역시</option>");
Webclient.println("</select>");
Webclient.println("<input type=\"submit\">");
Webclient.println("</form>");
Webclient.println("<p>Country : " + country + "</p>");
Webclient.println("<p>City Name : " + cityName + "</p>");
Webclient.println("<p>Longitude : " + coordLon + "</p>");
Webclient.println("<p>Latitude : " + coordLat + "</p>");
Webclient.println("<p>Sun Rise : " + sunRise + "</p>");
Webclient.println("<p>Sun Set : " + sunSet + "</p>");
Webclient.println("<p>Temperature : " + tempValue + "</p>");
Webclient.println("<p>Humidity : " + humidValue + "</p>");
Webclient.println("<p>Pressure : " + pressValue + "</p>");
Webclient.println("</body>");
Webclient.println("</html>");

그 다음 열두 줄은 도시명을 입력 받기 위한 폼 태그입니다. 웹페이지에서 드롭다운 리스트 방식으로 도시명을 제공하고 또 입력 받았습니다. 웹페이지로 접속한 클라이언트는 도시명에 대한 정보가 없기 때문에 일일이 제공해주어야 하지만, 아이폰 앱에선 미리 코딩을 해주면 되므로 역시 훨씬 단순하게 수정하겠습니다.

우선, 모든 도시 리스트를 보낼 필요가 없습니다. 현재 검색된 도시명 하나만 보내면 됩니다. 또, 한글 도시명을 보낼 필요가 없기 때문에 City ID만 보내겠습니다.

Webclient.println("<cityid>");
Webclient.println("1835847");
Webclient.println("</cityid>");

서울의 City ID 값을 이용한 태그입니다.  이렇게 Form 부분은 간단하게 정리 되었습니다. 물론, City ID값을 위처럼 고정하면 안되기 때문에 아래와 같이 입력해야 합니다.

Webclient.println("<cityid>");
Webclient.println(cityID);
Webclient.println("</cityid>");

프로그램 처음에, String cityID = "1835847"로 선언한 부분이 있습니다. 초기값으로 서울의 ID값을 주고, 클라이언트로부터 요청이 올 때마다 여기에 변경된 City ID값을 저장하여 프로그램내에서 사용합니다. 이 변수를 그대로 적용하여 7개의 도시 중에 선택하여 제공할 수 있도록 해야 합니다.

또, println() 함수는 줄바꿈이 없는 print() 함수로 변경했습니다. HTML 문서일 경우 웹 브라우저에서 줄바꿈 문자를 무시하지만, iOS의 XML 처리기는 입력 받은 그대로 반환하기 때문에 따로 처리하는 것 보다는 처음부터 안보내는 게 더 좋을 듯 합니다.

test example 완성 및 테스트

Webclient.print("<cityid>");
Webclient.print(cityID);
Webclient.print("</cityid>");
Webclient.print("<p>Country : " + country + "</p>");
Webclient.print("<p>City Name : " + cityName + "</p>");
Webclient.print("<p>Longitude : " + coordLon + "</p>");
Webclient.print("<p>Latitude : " + coordLat + "</p>");
Webclient.print("<p>Sun Rise : " + sunRise + "</p>");
Webclient.print("<p>Sun Set : " + sunSet + "</p>");
Webclient.print("<p>Temperature : " + tempValue + "</p>");
Webclient.print("<p>Humidity : " + humidValue + "</p>");
Webclient.print("<p>Pressure : " + pressValue + "</p>");
Webclient.println("</body>");
Webclient.println("</html>");

이제 나머지 데이터 출력 부분을 수정하겠습니다. 위에서 표시한 부분이고, <cityid> 태그와 비슷하게 생겼기 때문에 수정도 쉽습니다. 출력하는 부분에서 가운데 변수명들은 그대로 두면 되고, 앞 뒤쪽을 태그로 만들어 주면 됩니다. 그리고, 도시명을 출력하는 city Name부분과 마지막 두 줄의 태그는 필요없으므로 삭제합니다.

Webclient.print("<'?xml version=\"1.0\" encoding=\"UTF-8\"?>");
Webclient.print("<weather>");
Webclient.print("<cityid>");
Webclient.print(cityID);
Webclient.print("</cityid>");
Webclient.print("<country>");
Webclient.print(country);
Webclient.print("</country>");
Webclient.print("<longitude>");
Webclient.print(coordLon);
Webclient.print("</longitude>");
Webclient.print("<latitude>");
Webclient.print(coordLat);
Webclient.print("</latitude>");
Webclient.print("<sunrise>");
Webclient.print(sunRise);
Webclient.print("</sunrise>");
Webclient.print("<sunset>");
Webclient.print(sunSet);
Webclient.print("</sunset>");
Webclient.print("<temperature>");
Webclient.print(tempValue);
Webclient.print("</temperature>");
Webclient.print("<humidity>");
Webclient.print(humidValue);
Webclient.print("</humidity>");
Webclient.print("<pressure>");
Webclient.print(pressValue);
Webclient.print("</pressure>");
Webclient.print("</weather>");

위와 같이 간단히 작성하였습니다. HTML 처럼 서식이나 레이아웃과 관련된 내용이 필요 없고 전송할 데이터 부분만 다루면 됩니다. 코드 길이가 좀 길어졌을 뿐, 내용은 동일한 구조의 반복입니다.

첫째 줄은 버전과 인코딩 정보가 들어 있는 프롤로그입니다. 모두 디폴트 값이기 때문에 그대로 복사해서 넣으면 되고, 없어도 ios XML처리기가 알아서 잘 합니다.

#include <SPI.h>
#include <WiFi101.h>
char ssid[] = "TURTLE";
char pass[] = "yesican1";
String cityID = "1835847"; // 기본값은 서울
String appID = "7b08dfe35b4273bbe63604c75573cacf"; // 각 계정의 APP ID
String selected1 = "";
String selected2 = "";
String selected3 = "";
String selected4 = "";
String selected5 = "";
String selected6 = "";
String selected7 = "";
bool tagInside = false;  // 태그 안쪽인지 바깥쪽인지 구별하는 변수
bool flagStartTag = false; // 스타트 태그인지 구별하는 변수
String currentTag = ""; // 현재 태그를 저장하기 위한 변수, 공백으로 초기화함
String currentData = ""; // 태그 사이의 컨텐츠를 저장하는 변수
String startTag = ""; // 현재 elements의 start tag 저장
String endTag = "";   // 현재 elements의 end tag 저장
String country = "";
String cityName = "";
String coordLon = "";
String coordLat = "";
String sunRise = "";
String sunSet = "";
String tempValue = "";
String humidValue = "";
String pressValue = "";
int status = WL_IDLE_STATUS;
char server[] = "api.openweathermap.org";
IPAddress ip;
WiFiServer Webserver(80); // Server 서비스를 위한 클래스 인스턴스
WiFiClient client;
void setup() {
  WiFi.setPins(8,7,4,2); 
  Serial.begin(9600);
  delay(1000);
  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue:
    while (true);
  }
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
    // wait 2 seconds for connection:
    delay(2000);
  }
  Serial.println("Connected to wifi");
  OpenWeatherMap();
  Webserver.begin();
  ip = WiFi.localIP();
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
}
void loop() {
  WiFiClient Webclient = Webserver.available();
  if (Webclient) {
    Serial.println("new client");
    bool newLine = false;
    String currentLine = "";
    while (Webclient.connected()) {
      if (Webclient.available()) {
        char c = Webclient.read();
        Serial.write(c);
        if (c == '\n') {
          if (newLine) {
            Serial.println("Client Request Ended!");
            Serial.println("Weather Data Request!");
            OpenWeatherMap(); 
            Serial.println("Web page transfer Start!");
            Webclient.println("HTTP/1.1 200 OK");
            Webclient.println("Content-type:text/html");
            Webclient.println("Connection: close");
            Webclient.println();
            Webclient.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            Webclient.print("<weather>");
            Webclient.print("<cityid>");
            Webclient.print(cityID);
            Webclient.print("</cityid>");
            Webclient.print("<country>");
            Webclient.print(country);
            Webclient.print("</country>");
            Webclient.print("<longitude>");
            Webclient.print(coordLon);
            Webclient.print("</longitude>");
            Webclient.print("<latitude>");
            Webclient.print(coordLat);
            Webclient.print("</latitude>");
            Webclient.print("<sunrise>");
            Webclient.print(sunRise);
            Webclient.print("</sunrise>");
            Webclient.print("<sunset>");
            Webclient.print(sunSet);
            Webclient.print("</sunset>");
            Webclient.print("<temperature>");
            Webclient.print(tempValue);
            Webclient.print("</temperature>");
            Webclient.print("<humidity>");
            Webclient.print(humidValue);
            Webclient.print("</humidity>");
            Webclient.print("<pressure>");
            Webclient.print(pressValue);
            Webclient.print("</pressure>");
            Webclient.print("</weather>");
            break;
          } else {
            newLine = true; 
            if (currentLine.indexOf("HTTP/1.1") != -1) {
              requestProcess(currentLine);
            }
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c;
          newLine = false;
        }
      }
    } 
    Webclient.stop();
    Serial.println("client disonnected");
  }
}
void requestProcess(String requestLine) {
  int citynameIndex = requestLine.indexOf("cityname");
  if (citynameIndex != -1) {
    cityID = requestLine.substring(citynameIndex + 9, citynameIndex + 16);
  }
  selected1 = "";
  selected2 = "";
  selected3 = "";
  selected4 = "";
  selected5 = "";
  selected6 = "";
  selected7 = "";
  if (cityID == "1835847") {
    selected1 = "selected";
  } else if (cityID == "1838519") {
    selected2 = "selected";
  } else if (cityID == "1835327") {
    selected3 = "selected";
  } else if (cityID == "1843561") {
    selected4 = "selected";
  } else if (cityID == "1841808") {
    selected5 = "selected";
  } else if (cityID == "1835224") {
    selected6 = "selected";
  } else if (cityID == "1833742") {
    selected7 = "selected";
  }
}
void OpenWeatherMap() { 
  Serial.println("\nStarting connection to server..."); 
  if (client.connect(server, 80)) {
    Serial.println("connected to server");
    String request = "GET /data/2.5/weather?id=";
    request += cityID;
    request += "&APPID=";
    request += appID;
    request += "&units=metric&mode=xml HTTP/1.1"; 
    client.println(request);
    client.println("Host: api.openweathermap.org");
    client.println("Connection: close");
    client.println();
  } 
  while (client.connected()) {
    while (client.available()) {
      char c = client.read(); 
      if (c == '<') {
        tagInside = true;
      } 
      if (tagInside) {
        currentTag += c;
      } else if (flagStartTag) {
        currentData += c;
      } 
      if (c == '>') {
        tagInside = false;
        if (currentTag.startsWith("</")) {
          flagStartTag = false;
          endTag = currentTag; 
          if (startTag.indexOf("country") != -1) {
            if (endTag.indexOf("country") != -1) {
              Serial.print("Country : ");
              Serial.println(currentData);
              country = currentData;
            }
          }
          currentData = "";
        } else {
          flagStartTag = true;
          startTag = currentTag;
          startTagProcessing();
        }
        currentTag = "";
      }
    }
  }
  Serial.println();
  Serial.println("disconnecting from server.");
  client.stop();
}
void startTagProcessing() {
  if (startTag.startsWith("<city")) {
    int attribName = startTag.indexOf("name=");
    if (attribName != -1) {
      cityName = startTag.substring(attribName + 6);
      int quote = cityName.indexOf("\"");
      cityName = cityName.substring(0, quote);
      Serial.println("City : " + cityName);
    }
  } else if (startTag.startsWith("<coord")) {
    int attribLon = startTag.indexOf("lon=");
    if (attribLon != -1) {
      coordLon = startTag.substring(attribLon + 5);
      int quote = coordLon.indexOf("\"");
      coordLon = coordLon.substring(0, quote);
      Serial.println("Longitude : " + coordLon);
    }
    int attribLat = startTag.indexOf("lat=");
    if (attribLat != -1) {
      coordLat = startTag.substring(attribLat + 5);
      int quote = coordLat.indexOf("\"");
      coordLat = coordLat.substring(0, quote);
      Serial.println("Latitude : " + coordLat);
    }
  } else if (startTag.startsWith("<sun")) {
    int attribTime = startTag.indexOf("rise=");
    if (attribTime != -1) {
      String riseHour = startTag.substring(attribTime + 17, attribTime + 19);
      String riseMin = startTag.substring(attribTime + 20, attribTime + 22);
      int tempValue = riseHour.toInt();
      tempValue -= 15;
      riseHour = String(tempValue);
      Serial.println("Sun Rise : " + riseHour + ":" + riseMin);
      sunRise = riseHour + ":" + riseMin;
    }
    attribTime = startTag.indexOf("set=");
    if (attribTime != -1) {
      String setHour = startTag.substring(attribTime + 16, attribTime + 18);
      String setMin = startTag.substring(attribTime + 19, attribTime + 21);
      int tempValue = setHour.toInt();
      tempValue += 9;
      setHour = String(tempValue);
      Serial.println("Sun Set : " + setHour + ":" + setMin);
      sunSet = setHour + ":" + setMin;
    }
  } else if (startTag.startsWith("<temperature")) {
    int attribValue = startTag.indexOf("value=");
    if (attribValue != -1) {
      tempValue = startTag.substring(attribValue + 7);
      int quote = tempValue.indexOf("\"");
      tempValue = tempValue.substring(0, quote);
      Serial.println("Temperature : " + tempValue);
    }
  } else if (startTag.startsWith("<humidity")) {
    int attribValue = startTag.indexOf("value=");
    if (attribValue != -1) {
      humidValue = startTag.substring(attribValue + 7);
      int quote = humidValue.indexOf("\"");
      humidValue = humidValue.substring(0, quote);
      Serial.println("Humidity : " + humidValue + "%");
    }
  }else if (startTag.startsWith("<pressure")) { 
    int attribValue = startTag.indexOf("value=");
    if (attribValue != -1) {
      pressValue = startTag.substring(attribValue + 7);
      int quote = pressValue.indexOf("\"");
      pressValue = pressValue.substring(0, quote);
      Serial.println("Pressure : " + pressValue + " hPa");
    }
  }
}

위에서 언급한 부분만 수정한 전체 소스입니다. 이제 HTML 문서가 아닌 XML 문서를 전송합니다. 여기까지의 소스를 가지고 다음 글에선 우선 아이폰에서 XML로 전송 받는 앱을 작성하겠습니다.

마지막으로 웹 브라우저를 통해 접속해서 XML데이터가 제대로 출력되고 있는지 확인해 보겠습니다.

우선, Mac에서 크롬으로 접속한 결과 화면입니다. XML인데도 웹 브라우저를 통해 데이터를 확인해 볼 수 있습니다. 왼쪽 소스의 하이라이트 부분 보면 Content-type이 text/html로 되어 있습니다. html을 xml로 변경하면 아래와 같이 결과가 바뀝니다.

컨텐츠 타입을 xml로 변경하면 크롬 브라우저가 이를 인식하여 XML문서로 파싱해 줍니다. 둘 다 아이폰에서의 결과는 동일하기 때문에 변경하지 않아도 상관 없습니다. 사파리에선 파싱하지 않고 그대로 출력합니다.

이상입니다.

Comments