loading
This Instructable was selected as a winner on both, "Sensors"and "Micro Controller" contests. Thanks a lot for all your votes! ;-)

Let's continuous our IoT exploration, with the NodeMCU. On this tutorial, we will develop a Home Weather Station, where we will display outdoor information as temperature and climate conditions including the present day and a 3 days forecast. Our station will also display indoor information as temperature and air humidity.

The above Block diagram gives us a general overview of the project.

At bellow video you can see the final project working:

Step 1: Bill of Material (BoM)

(All values are referencial in USD)

  1. NodeMCU ESP8266-12E ($8.79)
  2. 0.96" I2C IIC SPI Serial 128X64 White OLED LCD LED Display Module ($8.99)
  3. DHT22 AM2302 Temperature And Humidity Sensor ($9.88)
  4. Mini BreadBoard ($1.00)
  5. Male-Female Dupont Cables ($1.00)
  6. External 5V power Supply or battery

Step 2: Installing the OLED on NODEMCU

I assumed that you have your Arduino IDE already prepared with the required libraries to run the NodeMCU code. If not, please visit my awarded tutorial: From Blink to Blynk, an IoT Journey on the Wings of NodeMCU ESP-12E

Now it's time to install the OLED display, our old friend, SSD1306, wich main characteristics are:

  • Display size: 0.96"
  • I2C IIC SPI Serial
  • 128X64
  • White OLED LCD LED

Connect the OLED pins to the NodeMCU, as described bellow and shown at above electrical diagram:

  • SDA ==> D1 (5)
  • SCL* ==> D2 (4) * Also you can find "SDC" in the text
  • VCC ==> The SSD1306 can be powered with 5V (external) or 3.3V directly from the NodeMCU module.
  • GND ==> GND

Once we have connected the display, let's download and install its library on our Arduino IDE. We have use on previous projects the ACROBOT library version, but this time we will explore another one: The "ESP8266 OLED Driver for SSD1306 display" developed by Daniel Eichhorn (Make sure that you use Version 3.0.0 or bigger!).

Bellow the library that must be downloaded and installed on your Arduino IDE:

https://github.com/squix78/esp8266-oled-ssd1306

Once you re-started the IDE, the library should be already installed.

The library supports I2C protocol to access the OLED display using the built in Wire.h library:

#include <Wire.h>   
#include "SSD1306.h"
SSD1306  display(ADDRESS, SDA, SDC);

Let's list some important API that will be used with our OLED display. The complete list can be founded at the GITHub provided above.

A. Display Control:

void init(); // Initialise the display
void resetDisplay(void); // Cycle through the initialisation
void displayOn(void); // Turn the display on
void displayOff(void); // Turn the display offs
void clear(void); // Clear the local pixel buffer
void invertDisplay(void); // Inverted display mode
void normalDisplay(void); // Normal display mode
void setContrast(char contrast); // Set display contrast
void flipScreenVertically(); // Turn the display upside down

B. Pixel Drawing

void setColor(OLEDDISPLAY_COLOR color); // Sets the color of all pixel operations
void setPixel(int16_t x, int16_t y); // Draw a pixel at given position
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); // Draw a line from pos 0 to pos 1
void drawHorizontalLine(int16_t x, int16_t y, int16_t length); // Draw a line horizontally
void drawVerticalLine(int16_t x, int16_t y, int16_t length); // Draw a lin vertically
void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const char *image); // Draw a bitmap in the internal image format

C. Text Operations:

void drawString(int16_t x, int16_t y, String text); 
// Write the text at given position
uint16_t getStringWidth(const char* text, uint16_t length); 
// Returns the width of the const char* with the current font settings
uint16_t getStringWidth(String text); 
// Convenience method for the const char version
void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); 
// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH
void setFont(const char* fontData); 
// Sets the current font. 

// Available default fonts: ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 

D. Frames ("Ui Library")

The Ui Library is used to provide a basic set of Ui elements called, Frames and Overlays. A Frame is used to provide information the default behaviour is to display a Frame for a defined time and than move to the next (like "Pages"). The library also provides an Indicator that will be updated accordingly. An Overlay on the other hand is a pieces of information (e.g. a clock) that is displayed always at the same position.

void init(); // Initialise the display
void setTargetFPS(uint8_t fps); //Configure the internal used target FPS
void enableAutoTransition(); //Enable automatic transition to next frame
void disableAutoTransition(); // Disable automatic transition to next frame.
void setAutoTransitionForwards(); // Set the direction if the automatic transitioning
void setAutoTransitionBackwards(); // Set the direction if the automatic transitioning
void setTimePerFrame(uint16_t time); //Set the approx. time a frame is displayed
void setTimePerTransition(uint16_t time); //Set the approx. time a transition will take
void setFrameAnimation(AnimationDirection dir); //Configure what animation is used to transition
void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); //Add frame drawing functions
int8_t update();  // This needs to be called in the main loop

Once the both the OLED itself and its Library are installed, let's write a simple "Hello World program to test it. Enter with bellow code on your IDE, the result should be a display as shown at the above photo:

/* Hello World OLED Test */
#include   // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
SSD1306  display(0x3c, 5, 4); // Initialise the OLED display using Wire library
void setup() 
{
  Serial.begin(115200);
  display.init(); // Initialising the UI will init the display too.
  display.flipScreenVertically();
  
  display.clear();
  drawHelloWorld(); 
  display.display();
}
void loop() 
{
  
}
void drawHelloWorld() 
{
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.setFont(ArialMT_Plain_10);
    display.drawString(0, 0, "Hello world");
    display.setFont(ArialMT_Plain_16);
    display.drawString(0, 10, "Hello world");
    display.setFont(ArialMT_Plain_24);
    display.drawString(0, 26, "Hello world");
}

Let's now, upload the sketch: SSD1306SimpleDemo.ino that is part of EXAMPLE's Library. Before run the code, change the OLED pins connection accordantly:

// Initialise the OLED display using Wire library
SSD1306  display(0x3c, 5, 4);

The video bellow shows the OLED playing the demo test code:

Step 3: Getting Indoor Data

Our NodeMCU is talking with world now! So, let's give it something real to show up! We will install a Digital Temperature/Humidity type sensor. The old and good DHTxx (DHT11 or DHT22). The ADAFRUIT site provides great information about those sensors. Bellow, some information retrieved from there:

Overview

The low cost DHT temperature & humidity sensors are very basic and slow, but are great for hobbyists who want to do some basic data logging. The DHT sensors are made of two parts, a capacitive humidity sensor and a thermistor. There is also a very basic chip inside that does some analog to digital conversion and spits out a digital signal with the temperature and humidity. The digital signal is fairly easy to read using any microcontroller.

DHT11 vs DHT22

We have two versions of the DHT sensor, they look a bit similar and have the same pinout, but have different characteristics. Here are the specs:

DHT11

  • Ultra low cost
  • 3 to 5V power and I/O
  • 2.5mA max current use during conversion (while requesting data)
  • Good for 20-80% humidity readings with 5% accuracy
  • Good for 0-50°C temperature readings ±2°C accuracy
  • No more than 1 Hz sampling rate (once every second)
  • Body size 15.5mm x 12mm x 5.5mm
  • 4 pins with 0.1" spacing

DHT22

  • Low cost
  • 3 to 5V power and I/O
  • 2.5mA max current use during conversion (while requesting data)
  • Good for 0-100% humidity readings with 2-5% accuracy
  • Good for -40 to 125°C temperature readings ±0.5°C accuracy
  • No more than 0.5 Hz sampling rate (once every 2 seconds)
  • Body size 15.1mm x 25mm x 7.7mm
  • 4 pins with 0.1" spacing

As you can see, the DHT22 is a little more accurate and good over a slightly larger range. Both use a single digital pin and are 'sluggish' in that you can't query them more than once every second (DHT11) or two (DHT22).

Both sensors will work fine to get Indoor information to be displayed on our Home Weather Station.

The DHTxx has 4 pins (facing the sensor, pin 1 is the most left) :

  • VCC (we can connect to external 5V or to 3.3V from NodeMCU);
  • Data out;
  • Not Connected
  • Ground.

Once usually you will use the sensor on distances less than 20m, a 10K ohm resistor should be connected between Data and VCC pins. The Output pin will be connected to NodeMCU pin D3 (see the diagram above).

Once the sensor is installed at our module, download the DHT library from Adafruit github repository and install it in your Arduino's Library file. Once you reload your Arduino IDE, the "DHT sensor library" should be installed.

Let's define our sensor parameters and its associated variables (we will use at first, DHT22):

/* DHT22 */
#include "DHT.h"
#define DHTPIN D3  
#define DHTTYPE DHT22 
DHT dht(DHTPIN, DHTTYPE);
int localHum = 0;
int localTemp = 0;

Now, let's create a function to read its data:

/***************************************************
* Get indoor Temp/Hum data
****************************************************/
void getDHT()
{
  float tempIni = localTemp;
  float humIni = localHum;
  localTemp = dht.readTemperature();
 /* for returning Fahrenheit use localTemp = dht.readTemperature(true) */
  localHum = dht.readHumidity(); 
  if (isnan(localHum) || isnan(localTemp))   // Check if any reads failed and exit early (to try again).
  {
    Serial.println("Failed to read from DHT sensor!");
    localTemp = tempIni;
    localHum = humIni;
    return;
  }
}

Once we have the data, let's present them on our OLED Display:

/***************************************************
* Draw Indoor Page
****************************************************/
void drawDHT() 
{
  int x=0;
  int y=0;
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawString(0 + x, 5 + y, "Hum");
  
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawString(43 + x, y, "INDOOR");

  display.setFont(ArialMT_Plain_24);
  String hum = String(localHum) + "%";
  display.drawString(0 + x, 15 + y, hum);
  int humWidth = display.getStringWidth(hum);

  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawString(95 + x, 5 + y, "Temp");

  display.setFont(ArialMT_Plain_24);
  String temp = String(localTemp) + "°C";
  display.drawString(70 + x, 15 + y, temp);
  int tempWidth = display.getStringWidth(temp);
}

The above foto shows how the data will be shown at display.

You can download the complete code for indoor operation from my GitHub:
Home Weather Station Indoor code

Or using the file bellow:

Step 4: Outdoor Data: Weather Underground Service

Our Outdoor weather data will be provide by a free service, the Weather Underground. You will need to create an account on their website and get an Weather API key. Do it following the instructions at bellow link:

https://www.wunderground.com/weather/api

Our Weather station is based on the great work done by Daniel Eichhorn (@squix78). Follow the instructions on his GitHub to get the appropriated Libraries. You will need at least to install the libraries bellow:

After you have the libraries installed and the IDE re-started, download the program bellow from my GitHub:

MJRoBot Home Weather Station code

Once you have the code loaded on your Arduino IDE, open the "stationCredentials.h" and replace the dummy data with your personnel data:

/* WIFI */
const char* WIFI_SSID = "YOUR SSID";
const char* WIFI_PWD = "YOUR PASSWORD";

/* Wunderground Settings */
const boolean IS_METRIC = true; // use false for Fahrenheit 
const String WUNDERGRROUND_API_KEY = "YOUR KEY";
const String WUNDERGRROUND_LANGUAGE = "EN";
const String WUNDERGROUND_COUNTRY = "CL";
const String WUNDERGROUND_CITY = "Santiago";
Note that the weather information that we will retrieve from W.Underground service will be regarding Santiago (the city) and CL (the country: Chile). You must also change it with your own city data.

And that's it! Your station must be running now as you can see on my prototype's photos above.

Step 5: Putting the Station in the Box

The last stage is to assemble our station on the box. I only did a simple example as shown above. I also changed the DHT22 with DHT11, only for fun

Do not forget to change the appropriate DHT declaration on file "stationDefines.h".

If you need to change the time zone, go to stationDefines.h file and change the variable UTC_OFFSET:

const float UTC_OFFSET = -3;

That's all folks!

Step 6: Conclusion

As always, I hope this project can help others find their way in the exciting world of electronics and IoT!

Please visit my GitHub for updated files:

MJRoBot Home Weather Station depository

For more projects, please visit my blog: MJRoBot.org

Saludos from the south of the world!

See you at my next instructable!

Thank you

Marcelo

<p>Hello,</p><p>My weather station is displaying a time that is one hour ahead of the time in my city. Do you know how to fix this?</p><p>Also, does anyone know how to convert celsius to farenheit on in the code?</p>
<p>Another point, to change the information received from Wunderground site from Celsius to Fahrenheit, you must change the variable IS_METRIC, from true to false (this declaration is on file stationCredentials.h):</p><p>const boolean IS_METRIC = false;</p><p>You must also change the oC to oF at display routines at main code. </p>
<p>Hi. Go to the file: stationDefines.h and change the UTC_OFFSET variable for the one correspondent to your time zone. </p><table><tbody><tr><td>t in you/* TimeClient */</td></tr><tr><td>const float UTC_OFFSET = -3;</td></tr></tbody></table><p>I suppose that in your case, should be:</p><p>const float UTC_OFFSET = -4;</p><p>Regarding LOCAL temperature, you can get the DHT reading directly in Fahrenheit, changing the line:</p><p>localTemp = dht.readTemperature(); // Returnes Celcius</p><p>to</p><p>localTemp = dht.readTemperature(true); // Returns Fahrenheit.</p>
<p>Hello... A very nice project and I made already a first part, the indoor weather station, with some parts different. But now I have a problem: I'm not using a I2C OLED but a SPI one.</p><p>My main difference on the indoor weather station is (after a long time of trying):</p><p> #include &quot;SSD1306.h&quot;<br>#include &lt;SPI.h&gt;<br> #include &quot;SSD1306Spi.h&quot;</p><p>SSD1306Spi display(D0, D2, D8);</p><p>But with now I can't use your code for the outdoor weather station with my OLED, because you use other code that don't work with my OLED.</p><p>I'm new on this matter and I'm learning by myself, so can you help me?</p><p>Best regards.</p>
<p>Hi Luis, Sorry, but I am not sure that I understood your doubt. I suppose the is not working because everything was developed with this OLED in mind. From Underground come graphics for example, that must be worked to be presented on anothe display. Also note that must be a grphical one. </p>
<p>Fun, cheap, and easy build. Great instructions! I'll note that I couldn't get the API data using my city's name, but it works great after I replaced the name with a 5-digit US zip code. Used a DHT11 b/c it's what I had on hand. Only issue is the C to F conversion on my indoor temp is returning two decimal places instead of 1 (like out), but i'll figure out eventually.</p>
<p>really Great!!!! ;-)</p>
<p>nice project man. is it possible to use bunch of max7219 modules instead of OLED module?</p>
Yes. The NodeMCU can drive the Max7219. But you will have a lot of work to write the code to present letter by letter the date on displays. I really think that such weather station application is more suatable for a graphic display.<br><br>Anyway, you can for example look on this link for more information:<br>https://frightanic.com/iot/nodemcu-max7219-8x8-led-matrix-display/<br><br>Thanks<br>Marcelo<br>
<p>Very nice! It would be interesting to extend this, so one could have multiple local sensors - outdoor, indoor(s), etc.</p>
Just amazing, man. Maybe with a bigger LCD and a &quot;wallpaper&quot; would be fantastic.
<p>Or using a &quot;credit card sized&quot; <a href="http://www.ebay.com/itm/10PCS-Portable-Credit-Card-3X-Magnifier-Magnifying-Magnification-Fresnel-Lens/201222493286?_trksid=p2047675.c100005.m1851&_trkparms=aid%3D222007%26algo%3DSIC.MBE%26ao%3D2%26asc%3D20131003132420%26meid%3D690859c0024a450abd9a008d60ec4c29%26pid%3D100005%26rk%3D4%26rkt%3D6%26sd%3D250807879071" rel="nofollow">fresnel lens</a> (0.99 $ x 10 pcs) ! :-)</p>
???
<p>Sorry, Mario, my replay did not was complete. I Did not understand the &quot;wallpaper&quot; idea. </p><p>And thanks for your words! ;-)</p>
I meant a back ground image. To make it look like it's an Android widget or something. ;)<br><br>And no problems! :D
*Background
<p>nice little project! One comment, the NodeMCU's D3 and D4 pins both have internal pull-up resistors (12K believe) so you could probably drop the external pull-up and save some space.</p>
Good point. Usually we not define the pin used by the DHTxx once it is done directly by the library. I will check it. I read the the library alheady do this (set up the internal pull up resistor) if true, maybe we do not need a resistor at all! I took a quick look and did not not conformed it, but I will go deeper, doing some tests.
<p>Beautifully done !</p>
Thanks a lot! ;-)
<p>Emm... if you use WIS, why you need sensor?? (and opposite)</p>
First of all I can not have weather condition and also forescast it for some days only using internal sensors. By the other side, web services can not delivery data about the temperature inside my house. That's because both data are complementary and not redundant.
<p>Nice project! By the way, have you ever considered to use BME280 instead of DHT22 as it will let you to check pressure and doint it to predict weather changes. And it's even cheaper than DHT if you will search for it on eBay.</p>
Thanks a lot and good point. I never tried the BME280, but certainly I will give it a try. Thanks for this! ;-)

About This Instructable

28,983views

380favorites

License:

Bio: Engineer, writer and forever student. Passionate to share knowledge of electronics with focus on IoT and robotics.
More by mjrovai:IoT Made Simple: Servo Control With NodeMCU and Blynk Arduino Color Detection WiFi Voice Controlled Robot With NodeMCU 
Add instructable to: