January 24, 2016

Arduino OpenWeatherMap Client

This project requires Arduino UNO, Arudino Ethernet Shield, and a 1602 LCD display. My plan is to build a simple OpenWeatherMap client using Arduino UNO + Ethernet Shield and output weather information to LCD.

OpenWeatherMap Signup

We have to get an APPID from OpenWeatherMap to use their Weather API. The free account is sufficient for this project.

Arduino Ethernet Library

Arduino website has a complete reference to the Ethernet library. This library allows an Arduino board to connect to the Internet. It can serve as either a server or a client. For this project, we are acting like a web client. HttpClient is a pretty decent library to make my life easier to interact with the underlying Ethernet library. For demonstration purpose, I have removed all error checking here.

static int http_get(const char *server, const char *url,
                    char *buf, size_t size)
{
  // TODO ensure buf and size are valid
  EthernetClient client;
  HttpClient http(client);

  int error = http.get(server, url);
  // TODO check error code
  error = http.responseStatusCode();
  // TODO check error code
  error = http.skipResponseHeaders();
  // TODO check error code

  int len = http.contentLength();
  size_t index = 0;
  while (http.connected() || http.available()) {
    if (http.available()) {
      char c = http.read();
      buf[index++] = c;

      if (index >= size)
        break;
    } else {
      // wait for data to arrive in milliseconds
      delay(200);
    }
  }
  buf[index] = 0;

  client.flush();
  http.stop();

  return len;
}

Responses are copied to an external buffer for later parsing.

Parsing Weather Response

OpenWeatherMap responses are in JSON format. ArduionJson becomes very handy to parse the weather data. One thing HttpClient can not handle is the HTTP 1.1 chunk data. Additional work (hack) is needed to truncate the buffer to get the first chunk.

static void weather_request()
{
  char buf[1024] = { 0 };
  const char *server = "api.openweathermap.org";
  const char *url = "/data/2.5/weather?q=YOUR_CITY&units=metric&APPID=YOUR_OPENWEATHERMAP_APPID";

  if (http_get(server, url, buf, sizeof(buf)) >= 0) {
    char *p = buf;
    int len = 0;

    // handle http 1.1 chunk data
    // calculate first chunk size
    while (*p != '\r') {
      len = len * 16 + hex_to_dec(*p);
      ++p;
    }
    ++p;  // '\n'

    // truncate the first chunk
    *(p + len) = 0;

    // json parser
    StaticJsonBuffer<1024> json_buf;
    JsonObject &root = json_buf.parseObject(p);

    const char *description = root["weather"][0]["description"];
    int humidity = root["main"]["humidity"];
    int temp = (int) root["main"]["temp"];
    int temp_min = (int) root["main"]["temp_min"];
    int temp_max = (int) root["main"]["temp_max"];

    // TODO writes to LCD
  }
}

Please note that I use 1024 (1 KB) as the JSON buffer, which is a considerably large chunk of memory for Arduino UNO. Due to SRAM limitation (2 KB) on Arduino UNO and depending on the response size, you may get corrupted data. Arduino MEGA 2560 is recommended. Maybe there is a better and more efficient way to parse the weather data?

© 2015-2019 Jiawei Huang

Powered by Hugo & Kiss.