IoT Made Simple: Home Weather Station With NodeMCU and OLED





Introduction: IoT Made Simple: Home Weather Station With NodeMCU and OLED

About: Engineer, writer and forever student. Passionate to share knowledge of electronics with focus on IoT and robotics.
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:

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); 
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() 
  display.init(); // Initialising the UI will init the display too.
void loop() 
void drawHelloWorld() 
    display.drawString(0, 0, "Hello world");
    display.drawString(0, 10, "Hello world");
    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:


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:


  • 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


  • 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 
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;

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.drawString(0 + x, 5 + y, "Hum");
  display.drawString(43 + x, y, "INDOOR");

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

  display.drawString(95 + x, 5 + y, "Temp");

  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:

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 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:

Saludos from the south of the world!

See you at my next instructable!

Thank you


Microcontroller Contest 2017

Runner Up in the
Microcontroller Contest 2017

Sensors Contest 2017

Runner Up in the
Sensors Contest 2017

5 People Made This Project!


  • Clocks Contest

    Clocks Contest
  • Make it Move Contest

    Make it Move Contest
  • Casting Contest

    Casting Contest

We have a be nice policy.
Please be positive and constructive.


4 Questions


Hello, I am getting that error.

invalid conversion from 'const char*' to 'const uint8_t* {aka const unsigned char*}' [-fpermissive]

I include WeatherStationImages.h and WeatherStationFonts.h

problem is display->setFont(Meteocons_Plain_21);

I hope you have got some answers, thanks.


I solved the problem.

I can not get weather forecast, I receiced two Key Word. I tried both but in the pane is three times N/A

In this application I get in line 22 I get this error: "IS_METRIC' was not declared in this scope"
What is the solution?
Thanks Renzo


Problem soved compiling in PC with Win10, while I didn't succed with Win XP. Thank

2 more answers


You must compile the code with all 5 files together (there are 4 .h files with definitions) and not only the "Home_Weather_Station_Final.ino".

On line 6 of stationCredentials.h you have: const boolean IS_METRIC = true;


Hi Renzo. In Step 2? It is not clear. Please give more details. Thanks

Can I use a button to navigate through the pages.
Eg:- Press the button to go to the next page.
Plz help


Thanks Ok, but the error is "In file included from Home_Weather_Station_Final.ino:10:0:
C:\Programmi\Arduino\Schetcbook\MJRoBot-Home-Weather-Station-master\Home_Weather_Station_Final\stationCredentials.h:6:7: error: 'boolean' does not name a type
const boolean IS_METRIC = true;" ander after this WUNDERGRROUND_API_KEY = "YOUR KEY"; and so on

Try change to const bool IS_METRIC = true

May it works, but this new:"C:\Programmi\Arduino\Schetcbook\MJRoBot-Home-Weather-Station-master\Home_Weather_Station_Final\stationCredentials.h:7:7: error: 'String' does not name a type

Thanks for the tutorial. I keep getting a bunch of stray errors when trying to compile the Final Code
I would appreciate your Help.

Home_Weather_Station_Final:25: error: stray '\267' in program

MJRoBot-Home-Weather-Station/Home_Weather_Station_Final.ino at master · Mjrovai/MJRoBot-Home-Weather-Station · GitHub

1 reply

In your shetch , the line 22 says: "WundergroundClient wunderground(IS_METRIC);, but I get an error :"IS_METRIC' was not declared in this scope".

The article is interessant and well explained but owing to this error I can't compile the schetch.

I have added instructed libraries.But, on compiling provided code following error message is generated-(although, instruction '

#include <Adafruit_Sensor.h>' is not in code). Please help.

Arduino: 1.8.5 (Windows Store (Windows 10), Board: "NodeMCU 0.9 (ESP-12 Module), 80 MHz, 115200, 4M (3M SPIFFS)"
In file included from C:\Users\Dell\Documents\Arduino\Weather_Station_Muzaffarnagar\sketch_jan09a\sketch_jan09a.ino:2:0:
C:\Users\Dell\Documents\Arduino\libraries\DHT-sensor-library-master/DHT_U.h:25:29: fatal error: Adafruit_Sensor.h: No such file or directory

#include <Adafruit_Sensor.h>


compilation terminated.

exit status 1
Error compiling for board NodeMCU 0.9 (ESP-12 Module).

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

1 reply

Hi. Seems that your Arduino IDE has not the proper DHT library (orbit is incomplete for some reason). You can include it from Library manager on your IDE or downloading it from Adafruit GotHub:

Is there any way i could put this into deep sleep and wake it up pressing a button?

Where is the code that governs the transitions between frames? I know its a small change but I'd like to make the slide/scroll transition go right -> left instead of left -> right. I've looked through the code and cant seem to locate it.

2 replies

Hi, i found a solution. Go to OLEDDisplay.h. At line 115, change SLIDE_RIGHT to SLIDE_LEFT. There you go!

Hi Timothy,
The sequence to be present (left to right) goes on frame content: frame[0] => frame[1], => frame[2], etc.
The content is defined on the line bellow. If you change the position of frames inside frames[], the frames presentation will change:

/* frames */
FrameCallback frames[] = { drawDateTime,
The control of frames transition is got by UI Library. You would need change it to get a different direction of changing. I never tried do it.
Please refer to:

Time received from api for my city is half an hour Less than real time.

how to solve this problem?

1 reply

Hi. I never tried, but I suppose that you can do the same with "complete" hour change.

Go to the file: stationDefines.h and change the UTC_OFFSET variable for the one correspondent to your time zone.

/* TimeClient */
const float UTC_OFFSET = -3;

I do not know your country or time zone, but I suppose that in your case, should be like India:

const float UTC_OFFSET = +5.5;

Hello mjrovai! I made it ! It´s Fantastic ! I had some problem making it work with the OLED Display 1.3" ! But I´m done !

Best regards!


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?

Also, does anyone know how to convert celsius to farenheit on in the code?

3 replies

Hi. Go to the file: stationDefines.h and change the UTC_OFFSET variable for the one correspondent to your time zone.

t in you/* TimeClient */
const float UTC_OFFSET = -3;

I suppose that in your case, should be:

const float UTC_OFFSET = -4;

Regarding LOCAL temperature, you can get the DHT reading directly in Fahrenheit, changing the line:

localTemp = dht.readTemperature(); // Returnes Celcius


localTemp = dht.readTemperature(true); // Returns Fahrenheit.

Thank You,

This device works perfectly now. Great tutorial, and thank you for the help.

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):

const boolean IS_METRIC = false;

You must also change the oC to oF at display routines at main code.

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.

My main difference on the indoor weather station is (after a long time of trying):

#include "SSD1306.h"
#include <SPI.h>
#include "SSD1306Spi.h"

SSD1306Spi display(D0, D2, D8);

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.

I'm new on this matter and I'm learning by myself, so can you help me?

Best regards.

1 reply

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.