Displays the current weather, date, and time

Dependencies:   mbed MbedJSONValue mbed-rtos 4DGL-uLCD-SE ESP8266NodeMCUInterface

Welcome to the Smart Weather Clock, which uses WiFi and Bluetooth to give the user a clear date, time, and weather for the day.

This wiki serves to document

  • What components you'll need
  • How these components will be connected
  • How the code gets all the information it needs

Components

LCD Screen

Components / 4D Systems 128 by 128 Smart Color LCD uLCD-144-G2
128 by 128 Color Smart LCD with serial interface and SD card slot

The LCD screen is a 128x128 pixel full color LCD by 4D Systems. A wonderful introduction can be found here.

In the simplest case, we'll only use one screen, but we can (and perhaps should) augment the design to include two.

Import library4DGL-uLCD-SE

Fork of 4DGL lib for uLCD-144-G2. Different command values needed. See https://mbed.org/users/4180_1/notebook/ulcd-144-g2-128-by-128-color-lcd/ for instructions and demo code.

You can buy it from sparkfun.

5V Adapter

The adapter allows us to feed much more power to the MBED and, critically, decouples us from a computer. It's important to remember that on the barrel jack adapter (i.e., the part that goes in the breadboard), the pin closest to the jack is 5V, and the back pin is Ground.

You can get the adapter here, and the barrel jack adapter here

Bluetooth UART Module

This module allows us to interface with the mbed via bluetooth and the ever-helpful AdaFruit Bluetooth App. A great introduction can be found here.

We'll use it to allow the user to set some defaults and API keys. This is stored in persistent flash storage on the LPC 1768 (See the Local File System, but you'll need an SD card if you're using a different MBED device.

You can get the Bluetooth module here.

WiFi Module

The WiFi module is also from AdaFruit, like the Bluetooth module. This allows us to interface with different WiFi networks for internet connectivity. A nice introduction is here.

You can get the WiFi module here.

WiFi Interface!

The current WiFi interface (found here) has some major problems - we're using an augmented version in our code!

Connections

5V Input for MBED

Before you connect the MBED to 5V from the wall, Make sure you have connected it correctly! If you don't, you could brick the MBED.

Thankfully, this is easy to test - when you think you have hooked up the 5V Jack correctly, try lighting a single LED (with a 10K resistor in series). If it lights up - you're good!

Alternatively, you could just plug in the MBED via USB, and just not have the MBED run on the +5V. You'll still need the Wall Adapter, however.

WiFi Module

The WiFi Module has two Grounds - you must connect both!

MBED5V Barrel Jack
Vin+5V (back pin)
GndGnd (pin closest to jack)
MBEDuLCD Cable
5V5V
GndGnd
P09TX
P10RX
P11Reset
MBEDBluetooth UART
5VVin
GndGnd
GndCTS
P27TXO
P28RXI
MBEDWiFi Module
5VV+
GndGnd
GndGnd
P13RX
P14TX
P15Reset

Code

The code works in a fairly logical order:

  1. Connect to the given WiFi, and check that we have all of our API keys
  2. Get the current location via the IP API
  3. Get the current time via the TimeZoneDB
  4. Get the current weather via the OpenWeatherMap API
  5. Continually update the weather, once per minute

Some caveats:

  • Memory seems to be an issue. If I had to guess, it would be the MbedJSONValue JSON parser - instantiating a third one seems to always lead to an MBED OS Fault or an MBED Memory Allocation fault. More insight is needed here.
  • The off-the-shelf ESP8226 Interface doesn't handle receiving correctly - I had to augment the library with recv and recv2. It works in most simple practical use cases.
Revision:
6:ec7829f7cd38
Parent:
5:b77a717feada
Child:
8:d323c6406b47
--- a/main.cpp	Mon Apr 01 15:27:46 2019 +0000
+++ b/main.cpp	Mon Apr 01 16:54:53 2019 +0000
@@ -21,7 +21,6 @@
 // Bluetooth
 RawSerial pc(USBTX, USBRX);
 RawSerial dev(p28,p27);
-Thread dev_thread;
 
 // Error LED
 DigitalOut err_led(LED1);
@@ -80,55 +79,6 @@
     }   
 }
 
-/*
-void weather_updater() {
-    // get the weather
-    // first get the current weather
-    // Weather data is _long_
-    dev.printf("Hello, World!\n");
-
-    char forecast_buf[4096];
-    TCPSocketConnection forecast_sck;
-    // http://api.openweathermap.org/data/2.5/forecast?lat=33.7485&lon=-84.3871&appid=6971e1ebfcc60f29c8dcc617c532b1b6&cnt=8
-    forecast_sck.connect("api.openweathermap.org", 80);
-    char cmd[256];
-    sprintf(cmd,
-        "GET /data/2.5/weather?lat=%0.4f&lon=%0.4f&APPID=6971e1ebfcc60f29c8dcc617c532b1b6&cnt=8\r\nHost: api.openweathermap.org\r\n\r\n",
-        latitude, longitude);
-    forecast_sck.send_all(cmd, strlen(cmd));
-    wait(10);
-    int len_read = wifi.recv(forecast_buf, 4096 - 1, 0);
-    forecast_buf[len_read] = '\0';
-    dev.printf(forecast_buf);
-    return;
-    // http://api.openweathermap.org/data/2.5/forecast?lat=33.7485&lon=-84.3871&appid=6971e1ebfcc60f29c8dcc617c532b1b6
-    // Get current weather
-    char current_buf[4096];
-    sprintf(cmd,
-        "GET /data/2.5/forecast?lat=%0.4f&lon=%0.4f&APPID=%s\r\nHost: api.openweathermap.org\r\n\r\n",
-        latitude, longitude, weather_api_key);
-    sck.send_all(cmd, strlen(cmd));
-    wait(10);
-    int buf_len = wifi.recv(current_buf, 4096 - 1, 0);
-    current_buf[buf_len] = '\0';
-    // we'll always want to update the LCD - don't worry about the previous
-    // weather
-    int curr_temp = 0;
-    int high_temp = 0;
-    int low_temp = 0;
-    char buf[12];
-    sprintf(buf, "%d %d/%d", curr_temp, high_temp, low_temp);
-    // lock
-    lcd_lock.lock();
-    uLCD.text_width(2);
-    uLCD.text_height(2);
-    // include null!
-    uLCD.text_string(buf, 0, 5, FONT_7X8, WHITE);
-    // done! unlock
-    lcd_lock.unlock();
-}
-*/
-
 void dev_recv() {
     // Continually check if we have stuff...
     char buf[1024];
@@ -168,7 +118,9 @@
     lcd_lock.lock();
     uLCD.cls();
     uLCD.text_height(2);
-    uLCD.text_string("PLEASE WAIT", 0, 2, FONT_7X8, WHITE);
+    uLCD.text_width(2);
+    uLCD.text_string("PLEASE", 0, 2, FONT_7X8, WHITE);
+    uLCD.text_string("WAIT", 0, 5, FONT_7X8, WHITE);
     lcd_lock.unlock();
     
     // Need to get wifi settings. If we don't have local file, then ask!
@@ -323,14 +275,14 @@
         fprintf(fid, "%s\n%s\n%s\n%s\n%s\n", ssid, pass, ip_api_key, time_api_key, weather_api_key);
         fclose(fid);
     }
-    
+    /*
     dev.printf("\r\n** CREDENTIALS **\r\n");
     dev.printf("SSID: **%s** (%d)\r\n", ssid, strlen(ssid));
     dev.printf("PASS: **%s** (%d)\r\n", pass, strlen(pass));
     dev.printf("IP STACK: **%s**\r\n", ip_api_key);
     dev.printf("TIMEZONEDB: **%s**\r\n", time_api_key);
     dev.printf("WEATHERMAP: **%s**\r\n", weather_api_key);
-    
+    */
     bool res = wifi.init();
 
     // Set up the WiFi Access Point
@@ -353,10 +305,9 @@
     char buffer[BUF_SIZE];
     char cmd[512];
 
-    dev.printf("Getting the current location...\n");
     TCPSocketConnection tcp;
     tcp.connect("api.ipstack.com", 80);
-    //af9319bf6435ddd9bb640f763ff64d34
+    
     sprintf(cmd, 
         "GET /check?access_key=%s HTTP/1.1\r\nHost: api.ipstack.com\r\n\r\n",
         ip_api_key);
@@ -395,7 +346,7 @@
         }
     }
     buffer[footer_ind + 1] = '\0';
-    dev.printf(buffer);
+
     MbedJSONValue parser;
     parse(parser, buffer + header_ind);
     
@@ -405,9 +356,8 @@
     // Get the Time
     TCPSocketConnection time_tcp;
 
-    //http://api.timezonedb.com/v2.1/get-time-zone?key=YOUR_API_KEY&format=json&by=zone&zone=America/Chicago
     time_tcp.connect("api.timezonedb.com", 80);
-    //VFHNS0FSUJVN
+
     sprintf(cmd,
         "GET /v2.1/get-time-zone?key=%s&format=json&by=position&lat=%0.4f&lng=%0.4f HTTP/1.1\r\nHost: api.timezonedb.com\r\n\r\n",
         time_api_key,
@@ -415,7 +365,9 @@
         longitude);
 
     time_tcp.send_all(cmd, strlen(cmd));
+
     wait(5);
+
     read_len = wifi.recv(buffer, BUF_SIZE - 1, 0);
     buffer[read_len] = '\0';
     
@@ -456,27 +408,28 @@
     parse(time_parser, buffer + header_ind);
 
     // Add 5 so that we make up for the wait(5)
-    set_time(time_parser["timestamp"].get<int>() + 5);
+    // Add 3 so that we make up for TCP request - empirically...
+    set_time(time_parser["timestamp"].get<int>() + 5 + 3);
     // Now that we know what time it is, set up our Time Thread
     time_thread.start(time_updater);
+    
     TCPSocketConnection forecast_sck;
-    // http://api.openweathermap.org/data/2.5/forecast?lat=33.7485&lon=-84.3871&appid=6971e1ebfcc60f29c8dcc617c532b1b6&cnt=8
+
     forecast_sck.connect("api.openweathermap.org", 80);
-    //6971e1ebfcc60f29c8dcc617c532b1b6
+
     sprintf(cmd,
         "GET /data/2.5/weather?lat=%0.4f&lon=%0.4f&APPID=%s&cnt=8\r\nHost: api.openweathermap.org\r\n\r\n",
         latitude, longitude, weather_api_key);
     forecast_sck.send_all(cmd, strlen(cmd));
+
     wait(5);
-    int len_read = wifi.recv(buffer, BUF_SIZE - 1, 0);
-    buffer[len_read] = '\0';
-    
 
-    //buffer[footer_ind + 1] = '\0';
+    read_len wifi.recv(buffer, BUF_SIZE - 1, 0);
+    buffer[read_len] = '\0';
+    
+    // We don't have enough memory for another parser (why?), so just
+    // look for the word "temp" 0 hopefully won't have a city named "temp"!
     char* ind = strstr(buffer, "temp");
-    for (int m = 0; m < 15; m++) {
-        dev.putc(ind[m]);
-    }
     char num_buf[16];
     int num_ind = 0;
     // go until we find numbers
@@ -497,25 +450,21 @@
     }
     int temp = atoi(num_buf);
     // Convert
-    temp = (((temp - 273.15f) * 9.0f) / 5.0f) + 32.0f;
+    temp = kelvin2farenheit(temp);
     char temp_buf[12];
     sprintf(temp_buf, "   %dF", temp);
+    
     lcd_lock.lock();
     uLCD.text_width(2);
     uLCD.text_height(2);
     uLCD.text_string(temp_buf, 0, 5, FONT_8X8, WHITE);
-    // done! unlock
     lcd_lock.unlock();
-    
-    
-    // Start up weather service!
-    //weather_thread.start(weather_updater);
-    
-    // Listen on bluetooth.
-    //dev_thread.start(dev_recv);
+
+    time_t prev_time = time(NULL);
     while(true) {
         time_t curr_time = time(NULL);
-        if (true) {
+        // Wait for 60 seconds
+        if ((prev_time + 60) < curr_time) {
             sprintf(cmd,
                 "GET /data/2.5/weather?lat=%0.4f&lon=%0.4f&APPID=%s\r\nHost: api.openweathermap.org\r\n\r\n",
                 latitude, longitude, weather_api_key);
@@ -524,21 +473,14 @@
             forecast_sck.send_all(cmd, strlen(cmd));
             wait(5);
 
-            len_read = wifi.recv(buffer, BUF_SIZE - 1, 0);
+            read_len = wifi.recv(buffer, BUF_SIZE - 1, 0);
 
-            buffer[len_read] = '\0';
-
-    
-            dev.printf(buffer);
+            buffer[read_len] = '\0';
 
             ind = strstr(buffer, "temp");
-            for (int m = 0; m < 15; m++) {
-                dev.putc(ind[m]);
-            }
             num_ind = 0;
             // go until we find numbers
             while (char tmp = *ind++) {
-                dev.printf("CHAR: %c\r\n", tmp);
                 if (tmp > '0' && tmp < '9') {
                     num_buf[num_ind++] = tmp;
                     break;
@@ -554,19 +496,16 @@
                 }
             }
             temp = atoi(num_buf);
-            dev.printf("Temperature: %s (%d)\r\n", num_buf, temp);
+
             // Convert
-            temp = (((temp - 273.15f) * 9.0f) / 5.0f) + 32.0f;
+            temp = kelvin2farenheit(temp);
             sprintf(temp_buf, "   %dF", temp);
             lcd_lock.lock();
             uLCD.text_width(2);
             uLCD.text_height(2);
             uLCD.text_string(temp_buf, 0, 5, FONT_8X8, WHITE);
-            // done! unlock
             lcd_lock.unlock();
         }
-
-        err_led = !err_led;
     }
 }