Introduction: DIY World Clock and Weather Bot (Arduino + ESP8266)

Hi,

in this instuctable you will learn how to make world clock weather bot using Arduino Mega and ESP8266 (AT mode).

Features:

• personalized bot with LCD screen as mouth, potentiometer as nose and green LEDs as eyes
• choosing wanted city/country by using serial communication
• saving wanted city/country into EEPROM memory
• getting geolocation data, weather data and country code using OpenWeatherMap API and ESP8266
• getting local time using TimezoneDB API and ESP8266
• putting local time in a DS3231 real time clock module
• showing local time in real time and weather data on LCD screen
• showing chosen country code on LCD screen
• turning LEDs on according to the weather (yellow LED attached to sun picture goes on if it's a sunny day, blue LEDs go on and off according to how much does it rain etc.)


Requirements:

  • Arduino Mega - eBay
  • ESP8266 - eBay
  • Logic Level Converter - eBay
  • 3 x blue LED - eBay
  • 2 x green LED - eBay
  • 2 x white LED - eBay
  • yellow LED - eBay
  • DS3231 RTC (kinda optional but makes things much easier :) - eBay
  • 16X2 LCD screen (or bigger :) - eBay
  • wires, wires, wires - eBay
  • 9 x 220 ohm resistor - eBay
  • 10K ohm potentiometer - eBay
  • a box (plastic transparent box in this case)
  • white papers
  • adhesive tape
  • scalpel
  • crayons
  • Open Weather Map Api Key - you can get it for free here
  • TimezoneDB Api Key - you can get it for free here

Step 1: Preparing the Casing

First you will have to cut holes for LCD screen and potentiometer to fit in. LCD screen should present the mouth and potentiometer should present the nose so plain space accordingly. You should also cut those holes on a piece of paper. You should draw on a paper three rain drops for three blue LEDs, one Sun for a yellow LED, one cloud for a white LED and one or two thunderbolts for the other white LED. You can decorate eyes and nose in any way you want just remember to put green LEDs behind the eyes. If your box is not transparent, you should cut holes behind each of your drawing so light from LEDs comes out. When you are done, you can tape the papers on a box.

Step 2: Wiring

Since Arduino Mega is 5V and ESP8266 takes 3.3V you cannot (or at least it is not recommended) Arduino and ESP8266 directly. You should use Logic Level Converter (LLC) like it is used in this project or some other method to lower voltager for ESP8266.


Wiring ESP8266 to LLC

VCC -> LV

CH_PD -> LV

RST -> LV GND -> LV

GND Tx -> Tx

Rx -> Rx

Wiring Arduino to LLC

5V -> HV

3.3 -> LV

GND -> HV GND

GND -> LV GND

Tx -> Rx

Rx -> Tx

Wiring Arduino to DS3231 RTC

5V -> 5V

GND -> GND

SCL -> SCL

SDA -> SDA

Since LCD has many pins and it would take much space to describe how to connect LCD to Arduino I will not describe it but you can reference to the picture above. Same goes for the LEDs.

Step 3: Code

For the start you will need ssid and pass of your network, API key from OpenWeatherMap and TimezoneDB. Define those strings at the beggining of the code.

#define ssid "" //write your ssid
#define PASS "" //write your pass
#define owmIP "" //OpenWeatherMap api 
IP#define timeIP "" //timezoneDB api IP
#define owmKey ""; //OpenWeatherMap api Key
#define timeKey ""; //timezoneDB api Key

After that define your LED and LCD pins.

int rainLED[] = {31, 33, 35};
int eyesLED [] = {37, 39};
int sunLED = 41;<br>int cloudLED = 45;
int thunderLED = 43;

LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

In setup start LCD screen. serial communication between Arduino and PC (for choosing city/country) and Arduino and ESP8266. Also start the SDA/SCL communication with your RTC module. Baud rate of the depends on the model of the ESP8266. ESP8266 used in this example uses 115200 baud rate.

Wire.begin();

lcd.begin(16, 2);

Serial.begin(9600);
Serial1.begin(115200);

If there is a data in EEPROM memory use that data, if not as for the new data.

if (EEPROM.read(0) == 255) {
    Serial.println("ENTER CITY NAME (city or city,country):");
    setLCD("ENTER CITY NAME", "");
    bool isTxt = false;
    int br = 0;
    while (!isTxt) {     
    if (Serial.available() > 0)
    {
        while (Serial.available() > 0)
        {
          char c = char(Serial.read());
          if (c != '\n' && c != '\r') {
            city += c;
            EEPROM.write(br, byte(c));
           ++br;
          }
          delay(2);
        }
        isTxt = true;
      }
    }
  }
  else {
    int br = 0;
    while (EEPROM.read(br) != 255) {
      city += char(EEPROM.read(br));
      ++br;
    }
  }

Connecting the ESP8266 to the network using the AT commands. Since ESP8266 will be connecting to two web services you should send command AT+CIPMUX=1 to the ESP8266.

while (true) {
    Serial1.println("AT+RST");
    delay(5000);
    if (Serial1.find("OK")) {
      Serial.println("ESP8266 OK");
      setLCD("ESP8266 OK", "BOOTING UP...");
      if (connectWiFi()) {
        Serial.println("WIFI OK");
        setLCD("WIFI OK", "");
      }
      break;
    }
  }
  Serial1.println("AT+CIPMUX=1");

connectWiFi function:

boolean connectWiFi() {
  Serial1.println("AT+CWMODE=1");
  delay(2000);
  String cmd = "AT+CWJAP=\"";
  cmd += ssid;
  cmd += "\",\"";
  cmd += PASS;
  cmd += "\"";
  Serial1.println(cmd);
  delay(5000);
  if (Serial1.find("OK")) {
    return true;
  }
  else {
    return false;
  }
}

Connecting to the OpenWeatherMap service:

String cmd = "AT+CIPSTART=0,\"TCP\",\"";
cmd += owmIP;
cmd += "\",80";
Serial1.println(cmd);
delay(2000);
if (Serial1.find("Error")) {
  return;
}
cmd = "GET /data/2.5/weather?q=" + city + "&appid=" + owmKey + "&units=metric";
cmd += "\r\n";<br>  Serial1.print("AT+CIPSEND=0,");
Serial1.println(cmd.length());
if (Serial1.find(">")) {
  Serial1.print(cmd);
   ...
}

Connecting to the TimezoneDB service:

String cmd = "AT+CIPSTART=1,\"TCP\",\"";
cmd += timeIP;
cmd += "\",80";
Serial1.println(cmd);
delay(2000);
if (Serial1.find("Error")) {
  return;
  }
cmd = "GET /?key=" + timeKey + "&lat=" + lat + "&lng=" + lng + "&format=json  HTTP/1.1\r\nHost: api.timezonedb.com\r\n\r\n";
cmd += "\r\n";
Serial1.print("AT+CIPSEND=1,");
Serial1.println(cmd.length());
if (Serial1.find(">")) {
    Serial1.print(cmd);
    ...
}

Web services return data in XML or JSON format and there is a pretty good library for parsing JSON in Arduino but since there is a problem with getting the whole respone string we should get all the data manually by using function find().

while (!Serial1.find("\"lon\":")) {
      if ((millis() - currentMillis) > 10000) {
        again = true;
        return;
      }
    }
    while (true) {
      if (Serial1.available()) {
        char c = Serial1.read();
        if (c == ',') break;
        lng += c;
      }
    }
   ...

Depending on the weather data got from OWM service LEDs are turning on or off. Whis is pretty tricky for the rain effect because blue LEDs should be turning on and off (in example every second) asynchronous to the rest of the program (they should not block program). To achieve that effect you can use millis(). Part of code responsible for turning LEDs when it's normal rain in a chosen city:

else if (description == "light rain") {
      if (isSun) {
        pinMode(sunLED, OUTPUT);
        digitalWrite(sunLED, HIGH);
      }
      pinMode(cloudLED, OUTPUT);
      pinMode(rainLED[rainDrop], OUTPUT);
      digitalWrite(cloudLED, HIGH);
      digitalWrite(rainLED[rainDrop], HIGH);
      if ((millis() - weatherTime) > 1000) {
        digitalWrite(rainLED[rainDrop], LOW);
        ++rainDrop;
        weatherTime = millis();
      }

Functions for saving and getting time from the RTC module are taken from link and they are slightly modified for showing data to the LCD screen. Modified part of the returnTime() function:

char buffer1[10]; 
itoa(hour, buffer1, 10);
char buffer2[10];
itoa(minute, buffer2, 10);
if (hour < 10 && minute < 10) {
    return currentTime =  country + "  " + weekDay + "  0"  + buffer1 + ":0" + buffer2;
 }
else if (hour < 10) {
    return currentTime =  country + "  " + weekDay + "  0"  + buffer1 + ":" + buffer2;
  }
else if (minute < 10) {
    return currentTime =  country + "  " + weekDay + " "  + buffer1 + ":0" + buffer2;
  }
return currentTime =  country + "  " + weekDay + "  "  + buffer1 + ":" + buffer2;

Full code for this project can be found in a attachment.

I hope you enjoyed this Instructable and thank you for reading :) If you have any questions regarding this project feel free to contact me or ask me in the comments :)

Tech Contest

Participated in the
Tech Contest

Time Contest

Participated in the
Time Contest

Make It Glow! Contest

Participated in the
Make It Glow! Contest