ESP32 Weather Station Interface PCB Shield (Temperature, Humidity, Pressure, Date and Time)

In this project, you’ll learn how to build a Weather Station Interface PCB Shield for the ESP32 development board. The PCB features a BME280 temperature, humidity and pressure sensor, a light dependent resistor, a pushbutton, an OLED display and multiple WS2812B addressable RGB LEDs. The OLED displays the sensor readings and the LEDs produce different lighting effects to what is shown in the OLED. It also displays date and time.

ESP32 Weather Station Interface PCB Shield Temperature Humidity Pressure Date Time

Watch the Video Tutorial

This project is available in video format and in written format. You can watch the video below or you can scroll down for the written instructions.

Resources

You can find all the resources needed to build this project in the links below (or you can visit the GitHub project page):

Project Overview

ESP32 Weather Station Interface Project OVerview

Weather Station PCB Hardware Features

The shield is designed with some headers pins to stack the ESP32 board. For this reason, if you want to build and use our PCB, you need to get the same ESP32 development board. We’re using the ESP32 DEVKIT DOIT V1 board (the model with 36 GPIOs).

Stacked board PCB Weather Station Interface Shield

If you want to follow this project and you have a different ESP32 model, you can assemble the circuit on a breadboard or you can modify the PCB layout and wiring to match the pinout of your ESP32 board. Throughout this project, we provide all the necessary files, if you need to modify the PCB.

The shield consists of:

  • BME280 temperature, humidity and pressure sensor;
  • LDR (light dependent resistor – luminosity sensor);
  • 0.96 inch I2C OLED Display;
  • Pushbutton;
  • 12 WS2812B addressable RGB LEDs;

If you’re going to replicate this project on a breadboard, instead of individual WS2812B addressable RGB LEDs, you can use an addressable RGB LED strip or an addressable RGB LED ring with the same number of LEDs (12).

Weather Station PCB Pin Assignment

The following table shows the pin assignment for each component on the shield:

ComponentESP32 Pin Assignment
BME280GPIO 21 (SDA), GPIO 22 (SCL)
OLED DisplayGPIO 21 (SDA), GPIO 22 (SCL)
Light Dependent Resistor (LDR)GPIO 33
PushbuttonGPIO 18
Addressable RGB LEDsGPIO 27

Weather Station PCB Software Features

There are endless ways to program the same circuit to get different outcomes with different functionalities and features. In this particular project we’ll program the PCB as follows:

  • The OLED displays five different screens:
    1. Current date and time;
    2. Temperature
    3. Humidity
    4. Pressure
    5. Luminosity
  • Each screen is shown for 15 seconds before going to the next one.
  • Alternatively, you can press the pushbutton to change screens.
  • In each screen the WS2812B addressable RGB LEDs show a different pattern:
    • On the date and time screen, the RGB LEDs display a rainbow effect;
    • On the other screens, the RGB LEDs work like a gauge. For example, 100% humidity lights up all LEDs, 50% humidify lights up half of the number of LEDs.
    • The color of the LEDs is different for each screen: green for the temperature, blue for the humidity, purple for pressure and yellow for luminosity.

Testing the Circuit on a Breadboard

Before designing and building the PCB, it’s important to test the circuit on a breadboard. If you don’t want to make a PCB, you can still follow this project by assembling the circuit on a breadboard.

ESP32 Breadboard Weather Station Interface Shield Components Parts Required

Parts Required

To assemble the circuit on a breadboard you need the following parts (the parts for the actual PCB are shown in a later section):

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

After gathering all the parts, assemble the circuit by following the next schematic diagram:

ESP32 Weather Station Interface Schematic Diagram Fritzing

Designing the PCB

To design the circuit and PCB, we used EasyEDA which is a browser based software to design PCBs. If you want to customize your PCB, you just need to upload the following files:

Designing the circuit works like in any other circuit software tool, you place some components and you wire them together. Then, you assign each component to a footprint.

ESP32 Weather Station Interface PCB Schematic Diagram Wiring Circuit

Having the parts assigned, place each component. When you’re happy with the layout, make all the connections and route your PCB.

ESP32 PCB Weather Station Interface PCB Layout

Save your project and export the Gerber files.

Note: you can grab the project files and edit them to customize the shield for your own needs.

Ordering the PCBs at PCBWay

This project is sponsored by PCBWay. PCBWay is a full feature Printed Circuit Board manufacturing service.

Ordering the PCBs at PCBWay

Turn your DIY breadboard circuits into professional PCBs – get 10 boards for approximately $5 + shipping (which will vary depending on your country).

Once you have your Gerber files, you can order the PCB. Follow the next steps.

1. Download the Gerber files – click here to download the .zip file

2. Go to PCBWay website and open the PCB Instant Quote page. 

PCBWay Order PCB open instant quote page

3. PCBWay can grab all the PCB details and automatically fills them for you. Use the “Quick-order PCB (Autofill parameters)”.

PCBWay Order PCB autofill parameters

4. Press the “+ Add Gerber file” button to upload the provided Gerber files.

PCBWay Order PCB add gerber file button

And that’s it. You can also use the OnlineGerberViewer to check if your PCB is looking as it should.

ESP32 PCB Weather Station Interface Gerber Viewer Online

If you aren’t in a hurry, you can use the China Post shipping method to lower your cost significantly. In our opinion, we think they overestimate the China Post shipping time.

PCBWay Order PCB China post shipping method

You can increase your PCB order quantity and change the solder mask color. I’ve ordered the Blue color.

ESP32 PCB Weather Station Interface Final PCB PCBWay Order PCB

Once you’re ready, you can order the PCBs by clicking “Save to Cart” and complete your order.

Unboxing

After approximately one week using the DHL shipping method, I received the PCBs at my office.

ESP32 PCB Weather Station Interface Shield

As usual, everything comes well packed, and the PCBs are really high-quality. The letters on the silkscreen are really well-printed and easy to read. Additionally, the solder sticks easily to the pads.

We’re really satisfied with the PCBWay service. Here’s some other projects we’ve built using the PCBWay service:

Soldering the Components

The next step is soldering the components to the PCB. I’ve used SMD LEDs, SMD resistors and SMD capacitors. These can be a bit difficult to solder, but the PCB looks much better.

If you’ve never soldered SMD before, we recommend watching a few videos to learn how it’s done. You can also get an SMD DIY soldering Kit to practice a bit.

Here’s a list of all the components needed to assemble the PCB:

ESPCB Weather Station Interface Shield Components Parts Required

Here’s the soldering tools I’ve used:

TS80 Soldering Iron Review Best Portable Soldering Iron

Read our review about the TS80 Soldering Iron: TS80 Soldering Iron Review – Best Portable Soldering Iron.

Start by soldering the SMD components. Then, solder the header pins. And finally, solder the other components or use header pins if you don’t want to connect the components permanently.

Here’s how the ESP32 Shield looks like after assembling all the parts.

ESP32 PCB Weather Station Interface Final PCB

The ESP32 board should stack perfectly on the header pins on the other side of the PCB.

Stacked board PCB Weather Station Interface Shield

Programming the Weather Station Interface PCB

The code for this project displays sensors readings on different screens on the OLED display as well as date and time. The addressable RGB LEDs show different colors and animations accordingly to what is displayed on the screen.

You can program the PCB in any way that is more suitable for you.

We’ll program the ESP32 board using Arduino IDE. So make sure you have the ESP32 board add-on installed.

If you want to program the ESP32/ESP8266 using VS Code + PlatformIO, follow the next tutorial:

Installing Libraries (Arduino IDE)

For this project, you need to install all these libraries in your Arduino IDE.

All these libraries can be installed using the Arduino IDE library manager. Just go to Sketch > Include Library > Manage Libraries and search for the library name.

Installing Libraries (VS Code + PlatformIO)

If you’re programming the ESP32 using PlatformIO, you should include the libraries on the platformio.ini file like this:

[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
monitor_speed = 115200
lib_deps = adafruit/Adafruit NeoPixel @ ^1.7.0
           adafruit/Adafruit SSD1306 @ ^2.4.1
           adafruit/Adafruit Unified Sensor @ ^1.1.4
           adafruit/Adafruit BME280 Library @ ^2.1.2
           adafruit/Adafruit GFX Library @ ^1.10.3
           adafruit/Adafruit BusIO @ ^1.6.0

Code

Copy the following code to your Arduino IDE or to the main.cpp file if your using PlatformIO.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-weather-station-pcb/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <WiFi.h>
#include <time.h>

// Insert your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// NTP Server Details
const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 0;
const int   daylightOffset_sec = 3600;

// OLED Display
#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels

#define I2Cdisplay_SDA 21
#define I2Cdisplay_SCL 22
TwoWire I2Cdisplay = TwoWire(1);

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &I2Cdisplay, -1);

// WS2812B Addressable RGB LEDs
#define LED_PIN    27  // GPIO the LEDs are connected to
#define LED_COUNT  12  // Number of LEDs
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

// BME280
#define I2C_SDA 21
#define I2C_SCL 22
TwoWire I2CBME = TwoWire(0);
Adafruit_BME280 bme;

// LDR (Light Dependent Resistor)
#define ldr  33          

// Pushbutton
#define buttonPin  18    

int buttonState;              // current reading from the input pin
int lastButtonState = LOW;    // previous reading from the input pin

unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

// Screens
int displayScreenNum = 0;
int displayScreenNumMax = 4;

unsigned long lastTimer = 0;
unsigned long timerDelay = 15000;

unsigned char temperature_icon[] ={
  0b00000001, 0b11000000, //        ###      
  0b00000011, 0b11100000, //       #####     
  0b00000111, 0b00100000, //      ###  #     
  0b00000111, 0b11100000, //      ######     
  0b00000111, 0b00100000, //      ###  #     
  0b00000111, 0b11100000, //      ######     
  0b00000111, 0b00100000, //      ###  #     
  0b00000111, 0b11100000, //      ######     
  0b00000111, 0b00100000, //      ###  #     
  0b00001111, 0b11110000, //     ########    
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11111000, //    ##########   
  0b00001111, 0b11110000, //     ########    
  0b00000111, 0b11100000, //      ######     
};

unsigned char humidity_icon[] ={
  0b00000000, 0b00000000, //                 
  0b00000001, 0b10000000, //        ##       
  0b00000011, 0b11000000, //       ####      
  0b00000111, 0b11100000, //      ######     
  0b00001111, 0b11110000, //     ########    
  0b00001111, 0b11110000, //     ########    
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11011000, //    ####### ##   
  0b00111111, 0b10011100, //   #######  ###  
  0b00111111, 0b10011100, //   #######  ###  
  0b00111111, 0b00011100, //   ######   ###  
  0b00011110, 0b00111000, //    ####   ###   
  0b00011111, 0b11111000, //    ##########   
  0b00001111, 0b11110000, //     ########    
  0b00000011, 0b11000000, //       ####      
  0b00000000, 0b00000000, //                 
};

unsigned char arrow_down_icon[] ={
  0b00001111, 0b11110000, //     ########    
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11111000, //    ##########   
  0b00011100, 0b00111000, //    ###    ###   
  0b00011100, 0b00111000, //    ###    ###   
  0b00011100, 0b00111000, //    ###    ###   
  0b01111100, 0b00111110, //  #####    ##### 
  0b11111100, 0b00111111, // ######    ######
  0b11111100, 0b00111111, // ######    ######
  0b01111000, 0b00011110, //  ####      #### 
  0b00111100, 0b00111100, //   ####    ####  
  0b00011110, 0b01111000, //    ####  ####   
  0b00001111, 0b11110000, //     ########    
  0b00000111, 0b11100000, //      ######     
  0b00000011, 0b11000000, //       ####      
  0b00000001, 0b10000000, //        ##       
};

unsigned char sun_icon[] ={
  0b00000000, 0b00000000, //                 
  0b00100000, 0b10000010, //   #     #     # 
  0b00010000, 0b10000100, //    #    #    #  
  0b00001000, 0b00001000, //     #       #   
  0b00000001, 0b11000000, //        ###      
  0b00000111, 0b11110000, //      #######    
  0b00000111, 0b11110000, //      #######    
  0b00001111, 0b11111000, //     #########   
  0b01101111, 0b11111011, //  ## ######### ##
  0b00001111, 0b11111000, //     #########   
  0b00000111, 0b11110000, //      #######    
  0b00000111, 0b11110000, //      #######    
  0b00010001, 0b11000100, //    #   ###   #  
  0b00100000, 0b00000010, //   #           # 
  0b01000000, 0b10000001, //  #      #      #
  0b00000000, 0b10000000, //         #       
};

// Clear the LEDs
void colorWipe(uint32_t color, int wait, int numNeoPixels) {
  for(int i=0; i<numNeoPixels; i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(wait);                           //  Pause for a moment
  }
}

// Rainbow cycle along all LEDs. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  long firstPixelHue = 256;
    for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
      int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
}

// Create display marker for each screen
void displayIndicator(int displayNumber) {
  int xCoordinates[5] = {44, 54, 64, 74, 84};
  for (int i =0; i<5; i++) {
    if (i == displayNumber) {
      display.fillCircle(xCoordinates[i], 60, 2, WHITE);
    }
    else {
      display.drawCircle(xCoordinates[i], 60, 2, WHITE);
    }
  }
}

//SCREEN NUMBER 0: DATE AND TIME
void displayLocalTime(){
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");

  //GET DATE
  //Get full weekday name
  char weekDay[10];
  strftime(weekDay, sizeof(weekDay), "%a", &timeinfo);
  //Get day of month
  char dayMonth[4];
  strftime(dayMonth, sizeof(dayMonth), "%d", &timeinfo);
  //Get abbreviated month name
  char monthName[5];
  strftime(monthName, sizeof(monthName), "%b", &timeinfo);
  //Get year
  char year[6];
  strftime(year, sizeof(year), "%Y", &timeinfo);

  //GET TIME
  //Get hour (12 hour format)
  /*char hour[4];
  strftime(hour, sizeof(hour), "%I", &timeinfo);*/
  
  //Get hour (24 hour format)
  char hour[4];
  strftime(hour, sizeof(hour), "%H", &timeinfo);
  //Get minute
  char minute[4];
  strftime(minute, sizeof(minute), "%M", &timeinfo);

  //Display Date and Time on OLED display
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(3);
  display.setCursor(19,5);
  display.print(hour);
  display.print(":");
  display.print(minute);
  display.setTextSize(1);
  display.setCursor(16,40);
  display.print(weekDay);
  display.print(", ");
  display.print(dayMonth);
  display.print(" ");
  display.print(monthName);
  display.print(" ");
  display.print(year);
  displayIndicator(displayScreenNum);
  display.display();
  rainbow(10);
}

// SCREEN NUMBER 1: TEMPERATURE
void displayTemperature(){
  display.clearDisplay();
  display.setTextSize(2);
  display.drawBitmap(15, 5, temperature_icon, 16, 16 ,1);
  display.setCursor(35, 5);
  float temperature = bme.readTemperature();
  display.print(temperature);
  display.cp437(true);
  display.setTextSize(1);
  display.print(" ");
  display.write(167);
  display.print("C");
  display.setCursor(0, 34);
  display.setTextSize(1);
  display.print("Humidity: ");
  display.print(bme.readHumidity());
  display.print(" %");
  display.setCursor(0, 44);
  display.setTextSize(1);
  display.print("Pressure: ");
  display.print(bme.readPressure()/100.0F);
  display.print(" hpa");
  displayIndicator(displayScreenNum);
  display.display();
  int temperaturePer = map(temperature, -5, 36, 0, LED_COUNT-1);
  colorWipe(strip.Color(0,   255,   0), 50, temperaturePer);
}

// SCREEN NUMBER 2: HUMIDITY
void displayHumidity(){
  display.clearDisplay();
  display.setTextSize(2);
  display.drawBitmap(15, 5, humidity_icon, 16, 16 ,1);
  display.setCursor(35, 5);
  float humidity = bme.readHumidity();
  display.print(humidity);
  display.print(" %");
  display.setCursor(0, 34);
  display.setTextSize(1);
  display.print("Temperature: ");
  display.print(bme.readTemperature());
  display.cp437(true);
  display.print(" ");
  display.write(167);
  display.print("C");
  display.setCursor(0, 44);
  display.setTextSize(1);
  display.print("Pressure: ");
  display.print(bme.readPressure()/100.0F);
  display.print(" hpa");
  displayIndicator(displayScreenNum);
  display.display();
  int humidityPer = map(humidity, 0, 100, 0, LED_COUNT-1);
  colorWipe(strip.Color(0,   0,   255), 50, humidityPer);
}

// SCREEN NUMBER 3: PRESSURE
void displayPressure(){
  display.clearDisplay();
  display.setTextSize(2);
  display.drawBitmap(0, 5, arrow_down_icon, 16, 16 ,1);
  display.setCursor(20, 5);
  display.print(bme.readPressure()/100.0F);
  display.setTextSize(1);
  display.print(" hpa");
  display.setCursor(0, 34);
  display.setTextSize(1);
  display.print("Temperature: ");
  display.print(bme.readTemperature());
  display.cp437(true);
  display.print(" ");
  display.write(167);
  display.print("C");
  display.setCursor(0, 44);
  display.setTextSize(1);
  display.print("Humidity: ");
  display.print(bme.readHumidity());
  display.print(" hpa");
  displayIndicator(displayScreenNum);
  display.display();
  colorWipe(strip.Color(255,   0,   255), 50, 12);
}

// SCREEN NUMBER 4: LUMINOSITY
void displayLDR(){
  display.clearDisplay();
  display.setTextSize(2);
  display.drawBitmap(33, 5, sun_icon, 16, 16 ,1);
  display.setCursor(53, 5);
  int ldrReading = map(analogRead(ldr), 0, 4095, 100, 0);
  display.print(ldrReading);
  display.print(" %");
  display.setTextSize(1);
  display.setCursor(0, 34);
  display.print("Temperature: ");
  display.print(bme.readTemperature());
  display.print(" ");
  display.cp437(true);
  display.write(167);
  display.print("C");
  display.setCursor(0, 44);
  display.setTextSize(1);
  display.print("Humidity: ");
  display.print(bme.readHumidity());
  display.print(" %");
  display.setCursor(0, 44);
  displayIndicator(displayScreenNum);
  display.display();
  int ldrReadingPer = map(ldrReading, 0, 100, 0, LED_COUNT-1);
  colorWipe(strip.Color(255,   255,   0), 50, ldrReadingPer);
}

// Display the right screen accordingly to the displayScreenNum
void updateScreen() {
  colorWipe(strip.Color(0, 0, 0), 1, LED_COUNT);
  if (displayScreenNum == 0){
    displayLocalTime();
  }
  else if (displayScreenNum == 1) {
    displayTemperature();
  }
  else if (displayScreenNum ==2){
    displayHumidity();
  }
  else if (displayScreenNum==3){
    displayPressure();
  }
  else {
    displayLDR();
  }
}

void setup() {
  Serial.begin(115200);
  
  // Initialize the pushbutton pin as an input
  pinMode(buttonPin, INPUT);
  
  I2CBME.begin(I2C_SDA, I2C_SCL, 100000);
  I2Cdisplay.begin(I2Cdisplay_SDA, I2Cdisplay_SCL, 100000); 

  // Initialize OLED Display
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.clearDisplay();
  display.setTextColor(WHITE);
  
  // Initialize BME280
  bool status = bme.begin(0x76, &I2CBME);  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
  
  // Initialize WS2812B LEDs
  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)

  // Connect to Wi-Fi
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected.");
  
  // Init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
}

void loop() {
  // read the state of the switch into a local variable
  int reading = digitalRead(buttonPin);

  // Change screen when the pushbutton is pressed
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
      if (buttonState == HIGH) {
        updateScreen();
        Serial.println(displayScreenNum);
        if(displayScreenNum < displayScreenNumMax) {
          displayScreenNum++;
        }
        else {
          displayScreenNum = 0;
        }
        lastTimer = millis();
      }
    }
  }
  lastButtonState = reading;
  
  // Change screen every 15 seconds (timerDelay variable)
  if ((millis() - lastTimer) > timerDelay) {
    updateScreen();
    Serial.println(displayScreenNum);
    if(displayScreenNum < displayScreenNumMax) {
      displayScreenNum++;
    }
    else {
      displayScreenNum = 0;
    }
    lastTimer = millis();
  }
}

View raw code

We get date and time from an NTP server. So, the ESP32 needs to connect to the internet. Insert your network credentials on the following variables and the code will work straight away.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

How the Code Works

Read this section if you want to learn how the code works, or skip to the next section.

This code is quite long, but simple. If you want to fully understand how it works, you may need to take a look at the following tutorials:

Including Libraries

First, you need to include the necessary libraries.

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <WiFi.h>
#include <time.h>

Network Credentials

Insert your network credentials in the following lines so that the ESP32 can connect to your network to request date and time from an NTP server.

const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

NTP Server

Then, you need to define the following variables to configure and get time from an NTP server: ntpServer, gmtOffset_sec and daylightOffset_sec.

We’ll request the time from pool.ntp.org, which is a cluster of timeservers that anyone can use to request the time.

const char* ntpServer = "pool.ntp.org";

GMT Offset

The gmtOffset_sec variable defines the offset in seconds between your time zone and GMT. We live in Portugal, so the time offset is 0. Change the time gmtOffset_sec variable to match your time zone.

const long gmtOffset_sec = 0;

Daylight Offset

The daylightOffset_sec variable defines the offset in seconds for daylight saving time. It is generally one hour, that corresponds to 3600 seconds

const int daylightOffset_sec = 3600;

Learn more about getting time from NTP server: ESP32 NTP Client-Server: Get Date and Time (Arduino IDE)

OLED Display

The SCREEN_WIDTH and SCREEN_HEIGHT variables define the dimensions of the OLED display in pixels. We’re using a 0.96inch OLED display: 128 x 64 pixels.

#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels

The OLED display is connected to GPIO 22 (SCL) and GPIO 21 (SDA).

#define I2Cdisplay_SDA 21
#define I2Cdisplay_SCL 22
TwoWire I2Cdisplay = TwoWire(1);

Initialize a display object with the width and height defined earlier with I2C communication protocol (&I2Cdisplay).

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &I2Cdisplay, -1);

WS2812B Addressable RGB LEDS

You need to define the GPIO that the RGB LEDs are connected to. Addressable RGB LEDs communicate with the ESP32 using One Wire protocol. So, all LEDs can be controlled by the same GPIO. In this case, they are connected to GPIO 27.

#define LED_PIN 27

The LED_COUNT variable saves the number of addressable RGB LEDs we want to control. In this case, it’s 12.

#define LED_COUNT 12

Create an Adafruit_NeoPixel object called strip to control the addressable RGB LEDs.

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

BME280 Sensor

Create an Adafruit_BME280 object called bme on the default ESP32 pins.

#define I2C_SDA 21
#define I2C_SCL 22
TwoWire I2CBME = TwoWire(0);
Adafruit_BME280 bme;

LDR

Define the GPIO the LDR is connected to.

const int ldr = 33; // LDR (Light Dependent Resistor)

Pushbutton

Define the GPIO the pushbutton is connected to.

#define buttonPin 18

The following variables are used to handle the pushbutton.

int buttonState;              // current reading from the input pin
int lastButtonState = LOW;    // previous reading from the input pin

unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

OLED Screens

As mentioned previously, the OLED will display five different screens. Each screen is numbered from 0 to 4. The displayScreenNum variable holds the screen number that must be displayed on the OLED – it starts at zero. The displayNumMax holds the maximum number of screens.

int displayScreenNum = 0;
int displayScreenNumMax = 4;

The next variables will be used to handle the timer to display each screen for 15 seconds. You can change the display screen period on the timerDelay variable.

unsigned long lastTimer = 0;
unsigned long timerDelay = 15000;

Icons

In each screen, the OLED displays an icon related with the reading it is showing. In the temperature screen it displays a thermometer (temperature_icon), in the humidity screen a teardrop (humidity_icon), in the pressure screen a arrow (arrow_down_icon) and in the luminosity screen a sun (sun_icon). We need to include those icons in the code. These icons are 16×16 pixels.

unsigned char temperature_icon[] ={
  0b00000001, 0b11000000, //        ###      
  0b00000011, 0b11100000, //       #####     
  0b00000111, 0b00100000, //      ###  #     
  0b00000111, 0b11100000, //      ######     
  0b00000111, 0b00100000, //      ###  #     
  0b00000111, 0b11100000, //      ######     
  0b00000111, 0b00100000, //      ###  #     
  0b00000111, 0b11100000, //      ######     
  0b00000111, 0b00100000, //      ###  #     
  0b00001111, 0b11110000, //     ########    
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11111000, //    ##########   
  0b00001111, 0b11110000, //     ########    
  0b00000111, 0b11100000, //      ######     
};

unsigned char humidity_icon[] ={
  0b00000000, 0b00000000, //                 
  0b00000001, 0b10000000, //        ##       
  0b00000011, 0b11000000, //       ####      
  0b00000111, 0b11100000, //      ######     
  0b00001111, 0b11110000, //     ########    
  0b00001111, 0b11110000, //     ########    
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11011000, //    ####### ##   
  0b00111111, 0b10011100, //   #######  ###  
  0b00111111, 0b10011100, //   #######  ###  
  0b00111111, 0b00011100, //   ######   ###  
  0b00011110, 0b00111000, //    ####   ###   
  0b00011111, 0b11111000, //    ##########   
  0b00001111, 0b11110000, //     ########    
  0b00000011, 0b11000000, //       ####      
  0b00000000, 0b00000000, //                 
};

 unsigned char arrow_down_icon[] ={
  0b00001111, 0b11110000, //     ########    
  0b00011111, 0b11111000, //    ##########   
  0b00011111, 0b11111000, //    ##########   
  0b00011100, 0b00111000, //    ###    ###   
  0b00011100, 0b00111000, //    ###    ###   
  0b00011100, 0b00111000, //    ###    ###   
  0b01111100, 0b00111110, //  #####    ##### 
  0b11111100, 0b00111111, // ######    ######
  0b11111100, 0b00111111, // ######    ######
  0b01111000, 0b00011110, //  ####      #### 
  0b00111100, 0b00111100, //   ####    ####  
  0b00011110, 0b01111000, //    ####  ####   
  0b00001111, 0b11110000, //     ########    
  0b00000111, 0b11100000, //      ######     
  0b00000011, 0b11000000, //       ####      
  0b00000001, 0b10000000, //        ##       
};

 unsigned char sun_icon[] ={
  0b00000000, 0b00000000, //                 
  0b00100000, 0b10000010, //   #     #     # 
  0b00010000, 0b10000100, //    #    #    #  
  0b00001000, 0b00001000, //     #       #   
  0b00000001, 0b11000000, //        ###      
  0b00000111, 0b11110000, //      #######    
  0b00000111, 0b11110000, //      #######    
  0b00001111, 0b11111000, //     #########   
  0b01101111, 0b11111011, //  ## ######### ##
  0b00001111, 0b11111000, //     #########   
  0b00000111, 0b11110000, //      #######    
  0b00000111, 0b11110000, //      #######    
  0b00010001, 0b11000100, //    #   ###   #  
  0b00100000, 0b00000010, //   #           # 
  0b01000000, 0b10000001, //  #      #      #
  0b00000000, 0b10000000, //         #       
};

To learn more about how to display icons, we recommend reading our OLED guide: ESP32 OLED Display with Arduino IDE.

colorWipe() and rainbow() functions

The colorWipe() and rainbow() functions are used to control the addresable RGB LEDs.

The colorWipe() is used to light up or clear specific LEDs.

void colorWipe(uint32_t color, int wait, int numNeoPixels) {
  for(int i=0; i<numNeoPixels; i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(wait);                           //  Pause for a moment
  }
}

The rainbow() function, as the name suggests, displays a rainbow effect.

// Rainbow cycle along all LEDs. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  long firstPixelHue = 256;
    for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
      int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
}

You can learn more about these and other functions to control the strip by taking a look at the Neopixel Library’s examples.

displayIndicator() function

The displayIndicator() creates five little circles at the bottom of the display accordingly to the screen that is being displayed at the moment.

// Create display marker for each screen
void displayIndicator(int displayNumber) {
  int xCoordinates[5] = {44, 54, 64, 74, 84};
  for (int i =0; i<5; i++) {
    if (i == displayNumber) {
      display.fillCircle(xCoordinates[i], 60, 2, WHITE);
    }
    else {
      display.drawCircle(xCoordinates[i], 60, 2, WHITE);
    }
  }
}

The drawCircle(x, y, radius, color) function creates a circle. The fillCircle(x, y, radius, color) creates a filled circle.

We’re placing the center of the circles on the following x coordinates: 44, 54, 64, 74 and 84.

int xCoordinates[5] = {44, 54, 64, 74, 84};

We draw a filled circle for the current display and a “empty” circle for the other displays:

if (i == displayNumber) {
  display.fillCircle(xCoordinates[i], 60, 2, WHITE);
}
else {
  display.drawCircle(xCoordinates[i], 60, 2, WHITE);
}

Screen Number 0: Date and Time

The first screen that shows up on the OLED displays date and time. That’s what the displayLocalTime() function does. It also displays a rainbow effect on the addressable RGB LEDs.

//SCREEN NUMBER 0: DATE AND TIME
void displayLocalTime(){
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");

  //GET DATE
  //Get full weekday name
  char weekDay[10];
  strftime(weekDay, sizeof(weekDay), "%a", &timeinfo);
  //Get day of month
  char dayMonth[4];
  strftime(dayMonth, sizeof(dayMonth), "%d", &timeinfo);
  //Get abbreviated month name
  char monthName[5];
  strftime(monthName, sizeof(monthName), "%b", &timeinfo);
  //Get year
  char year[6];
  strftime(year, sizeof(year), "%Y", &timeinfo);

  //GET TIME
  //Get hour (12 hour format)
  /*char hour[4];
  strftime(hour, sizeof(hour), "%I", &timeinfo);*/
  
  //Get hour (24 hour format)
  char hour[4];
  strftime(hour, sizeof(hour), "%H", &timeinfo);
  //Get minute
  char minute[4];
  strftime(minute, sizeof(minute), "%M", &timeinfo);

  //Display Date and Time on OLED display
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(3);
  display.setCursor(19,5);
  display.print(hour);
  display.print(":");
  display.print(minute);
  display.setTextSize(1);
  display.setCursor(16,40);
  display.print(weekDay);
  display.print(", ");
  display.print(dayMonth);
  display.print(" ");
  display.print(monthName);
  display.print(" ");
  display.print(year);
  displayIndicator(displayScreenNum);
  display.display();
  rainbow(10);
}

Learn more about getting date and time from an NTP server with the ESP32: ESP32 NTP Client-Server: Get Date and Time (Arduino IDE)

Screen Number 1: Temperature

The second screen displays temperature. That’s done by calling the displayTemperature() function. This function also lights up the LEDs in a green color accordingly to the temperature value.

// SCREEN NUMBER 1: TEMPERATURE
void displayTemperature(){
  display.clearDisplay();
  display.setTextSize(2);
  display.drawBitmap(15, 5, temperature_icon, 16, 16 ,1);
  display.setCursor(35, 5);
  float temperature = bme.readTemperature();
  display.print(temperature);
  display.cp437(true);
  display.setTextSize(1);
  display.print(" ");
  display.write(167);
  display.print("C");
  display.setCursor(0, 34);
  display.setTextSize(1);
  display.print("Humidity: ");
  display.print(bme.readHumidity());
  display.print(" %");
  display.setCursor(0, 44);
  display.setTextSize(1);
  display.print("Pressure: ");
  display.print(bme.readPressure()/100.0F);
  display.print(" hpa");
  displayIndicator(displayScreenNum);
  display.display();
  int temperaturePer = map(temperature, -5, 36, 0, LED_COUNT-1);
  colorWipe(strip.Color(0,   255,   0), 50, temperaturePer);
}

Screen Number 2: Humidity

The displayHumidity() function is similar to the displayTemperature() function but displays the humidity value.

// SCREEN NUMBER 2: HUMIDITY
void displayHumidity(){
  display.clearDisplay();
  display.setTextSize(2);
  display.drawBitmap(15, 5, humidity_icon, 16, 16 ,1);
  display.setCursor(35, 5);
  float humidity = bme.readHumidity();
  display.print(humidity);
  display.print(" %");
  display.setCursor(0, 34);
  display.setTextSize(1);
  display.print("Temperature: ");
  display.print(bme.readTemperature());
  display.cp437(true);
  display.print(" ");
  display.write(167);
  display.print("C");
  display.setCursor(0, 44);
  display.setTextSize(1);
  display.print("Pressure: ");
  display.print(bme.readPressure()/100.0F);
  display.print(" hpa");
  displayIndicator(displayScreenNum);
  display.display();
  int humidityPer = map(humidity, 0, 100, 0, LED_COUNT-1);
  colorWipe(strip.Color(0,   0,   255), 50, humidityPer);
}

Screen Number 3: Pressure

The displayPressure() function is similar to the two previous functions, but display the pressure value.

// SCREEN NUMBER 3: PRESSURE
void displayPressure(){
  display.clearDisplay();
  display.setTextSize(2);
  display.drawBitmap(0, 5, arrow_down_icon, 16, 16 ,1);
  display.setCursor(20, 5);
  display.print(bme.readPressure()/100.0F);
  display.setTextSize(1);
  display.print(" hpa");
  display.setCursor(0, 34);
  display.setTextSize(1);
  display.print("Temperature: ");
  display.print(bme.readTemperature());
  display.cp437(true);
  display.print(" ");
  display.write(167);
  display.print("C");
  display.setCursor(0, 44);
  display.setTextSize(1);
  display.print("Humidity: ");
  display.print(bme.readHumidity());
  display.print(" hpa");
  displayIndicator(displayScreenNum);
  display.display();
  colorWipe(strip.Color(255,   0,   255), 50, 12);
}

Screen Number 4: Luminosity

Finally, the last screen displays the luminosity (displayLDR() function).

void displayLDR(){
  display.clearDisplay();
  display.setTextSize(2);
  display.drawBitmap(33, 5, sun_icon, 16, 16 ,1);
  display.setCursor(53, 5);
  int ldrReading = map(analogRead(ldr), 0, 4095, 100, 0);
  display.print(ldrReading);
  display.print(" %");
  display.setTextSize(1);
  display.setCursor(0, 34);
  display.print("Temperature: ");
  display.print(bme.readTemperature());
  display.print(" ");
  display.cp437(true);
  display.write(167);
  display.print("C");
  display.setCursor(0, 44);
  display.setTextSize(1);
  display.print("Humidity: ");
  display.print(bme.readHumidity());
  display.print(" %");
  display.setCursor(0, 44);
  displayIndicator(displayScreenNum);
  display.display();
  int ldrReadingPer = map(ldrReading, 0, 100, 0, LED_COUNT-1);
  colorWipe(strip.Color(255,   255,   0), 50, ldrReadingPer);
}

updateScreen() function

The updateScreen() function calls the right functions accordingly to the screen we want to display:

void updateScreen() {
  colorWipe(strip.Color(0, 0, 0), 1, LED_COUNT);
  if (displayScreenNum == 0){
    displayLocalTime();
  }
  else if (displayScreenNum == 1) {
    displayTemperature();
  }
  else if (displayScreenNum ==2){
    displayHumidity();
  }
  else if (displayScreenNum==3){
    displayPressure();
  }
  else {
    displayLDR();
  }
}

setup()

Set the button as an input.

pinMode(buttonPin, INPUT);

Initialize the OLED display.

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
  Serial.println(F("SSD1306 allocation failed"));
  for(;;);
}

Initialize the BME280 sensor:

if (!bme.begin(0x76)) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
}  

When the ESP32 first starts, we want to clear the OLED display. We also set the text color to white.

display.clearDisplay();
display.setTextColor(WHITE);

Initialize the WS2812B LEDs and set their brightness. You can change the brightness to any other value that best suits your enviroment.

strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show();            // Turn OFF all pixels ASAP
strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)

Initialize Wi-Fi and connect the ESP32 to your local network, so that the ESP32 can connect to the NTP server to get date and time.

// Connect to Wi-Fi
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected.");

Configure the NTP Server with the settings defined earlier.

configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
printLocalTime();

loop()

In the loop(), the following lines change the screen (displayScreenNum) every time the pushbutton is pressed.

// read the state of the switch into a local variable
int reading = digitalRead(buttonPin);

// Change screen when the pushbutton is pressed
if (reading != lastButtonState) {
  lastDebounceTime = millis();
}

if ((millis() - lastDebounceTime) > debounceDelay) {
  if (reading != buttonState) {
    buttonState = reading;
    if (buttonState == HIGH) {
      updateScreen();
      Serial.println(displayScreenNum);
      if(displayScreenNum < displayScreenNumMax) {
        displayScreenNum++;
      }
      else {
        displayScreenNum = 0;
      }
      lastTimer = millis();
    }
  }
}
lastButtonState = reading;

The next lines change between screens every 15 seconds (timerDelay).

if ((millis() - lastTimer) > timerDelay) {
  updateScreen();
  Serial.println(displayScreenNum);
  if(displayScreenNum < displayScreenNumMax) {
    displayScreenNum++;
  }
  else {
    displayScreenNum = 0;
  }
  lastTimer = millis();
}

Demonstration

After uploading the code to the board, press the on-board RESET button so that the ESP32 starts running the code.

ESP32 Weather Station Interface Project OVerview

For a complete demonstration, we recommend watching the following video.

Wrapping Up

We hope you’ve found this project interesting and you’re able to build it yourself. You can use PCBWay service and you’ll get a high quality PCB for your projects.

You can program the ESP32 with other code suitable for your needs. You can also edit the gerber files and add other features to the PCB or other sensors.

We have other similar projects that include building and designing PCBs that you may like:

Learn more about the ESP32 with our resources:

[Update] the bare PCB giveaway ended and the winners are: Etienne Bogaert, Wal Taylor, Charles Geiser, JM GIGAN and Kristian C.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

61 thoughts on “ESP32 Weather Station Interface PCB Shield (Temperature, Humidity, Pressure, Date and Time)”

  1. It is an exciting and very interesting project especially when monitoring data gathered from each sensor in real time.
    Thank you very much.

    Reply
  2. Hello,
    As always a nice project with pcb in prime and circle.
    I’ll be impressed with v2, no button (covid free) but only taking info with pir or ir detector or lux detector only. Many thanks for making me dreaming on what I will do when I will have more time. Take care

    Reply
  3. Whaouh
    What will be v2, no button (covid free), just presence detector. I know the is base and up to me to improve. Thank making me dreaming on what I can do whe I’ll have the time
    Take care

    Reply
  4. I like to read all the projects from the past. This one is again very interesting.
    I would suggest to modify the software to receive open weather data and display them after filtering.

    Thank you very much

    Reply
  5. It is a great Project that explains end-to-end from design to implementation with great documentation. It is exceptional to be able to execute it as it useful in day to day life. Coding sensors will be useful in variety of other projects. Thank you for creating and sharing your knowledge.

    Reply
  6. Congratulations Rui, really! Clear explanations and everything is very well demonstrated, as usual! I am currently working on a project where an electret microphone would capture the rain that falls to know whether it is raining or not, and transmit information on RGB led to see the intensity of the rain by different colors. Perhaps that data could also be incorporated…?

    Reply
  7. Hi Rui, its great to be able to have access to accurate and useful maker tutorials. My 9 yr old granddaughter is heavily into MakeCode and Scratch and is always checking my weather station so she would love to make this project. Even better still it would compliment her disco lights she in her bedroom. All the best.

    Reply
  8. What a great project.
    We would like to use (if you allow) this project for our CoderDojo Linz (Austria) when we work with kids electroic IoT. In our webpage we would include some descriptions. Thank you.

    Reply
  9. Nice project, and complete and clear description! I want to make 3 of my grandchildren (ages 11,9 and 6; 2 boys and 1 girl) aware of the technical possibilities. I hope at least of them gets this “virus”!

    Reply
  10. Nice project Rui, thanks for all of your help and tutorials.

    I like the use of the addressable RGB LED, it should give it a bit of bling 🙂

    I’ve added in a SD card to mine, so that I can supply the variable/configurable data (network credentials, station name, HTML template, etc.) and it seems to work well. I’ve got 4 of these devices scattered through my house and I’m planning on adding a couple of outdoor modules (with LORA added in).

    Happy Christmas

    Reply
  11. Another great project with lots of learning points and ideas for modifying my own projects.
    I have two OLEDs arriving tomorrow so cant wait to explore the changing screens part of the project. Thanks Rui

    Reply
  12. Very good project Rui. I think that on the same principle, it would be possible to build a compass with a magnetometer sensor HMC5883L. Many thanks for this.

    Gilles

    Reply
  13. I have so many new projects to build thanks to you. My latest is a mailbox/package delivery sensor that takes a picture and emails it to me. I would love to build this awesome Weather Station Interface since building a weather station is on my list of todo’s. Thanks for all you do Rui and Sara.
    Ron Stansell

    Reply
  14. I have a project for wind direction and speed in hand. I will try to add the outputs from that to another two display pages on the OLED

    Reply
  15. A beautiful project!
    I want to build it for use in the beach house.
    I certainly do it thanks to your simple and clear technical explanations.
    Regards,

    Ezio

    Reply
  16. I did the prototype on a breadboard. All works well with the exception that Temperature does not show up on screen 1 in large print next to the thermometer symbol. However the temperature is being recorded as it shows up on screen 2 and screen 3 in small print, but does not show on screen 4. Where it does not show OLED displays nan. Any idea of what is causing that?

    Reply
    • I just ran into the same thing when I prototyped the circuit.

      What you are seeing is the Adafruit BME280 library returning NAN (“not a number”) when it gets a 0x800000 reading back from the BME280. From various threads online, this may be related to voltage fluctuations/noise on the power lines of the BME280, timing issues, or the behavior of components on “inexpensive Chinese BME280 clones”.

      I lean toward timing issues, because I was able to change the behavior by simply adding some Serial.println(bme.readTemperature); lines to the change screen section at the bottom of the code. It was still unpredictable though, with bme.readTemperature() occasionally returning NAN on the other screens. Oddly enough, I have only seen this behavior from bme.readTemperature(), never from bme.readHumidity() or bme.readPressure()

      I cleared it up consistently by adding:

      while (temperature != temperature) {
      temperature = bme.readTemperature();
      }

      after line 242 (float temperature = bme.readTemperature();) in the original code, and adding similar code to the temperature portion of each of the other screen sections.

      The comparison (temperature != temperature) will evaluate “false” if bme.temperature() returned NAN (so the while retries), but will evaluate “true” for any normal value returned by bme.temperature() (so the while exits with a valid value).

      Reply
      • I too had this problem, and I agree that it may be a timing issue. I added your couple of lines of code and it cleared the problem completely, so thanks for that. As a matter of interest, I checked the accuracy of the BME module for its pressure output and for such a cheap part it is absolutely bang on. The pressure is very steady over the UK at the moment and the Met Office synoptic chart showed 1021 millibars for where I live. The BME280 was outputting 1011. As I am at an elevation of 300 feet and pressure drops at the rate of 1 millibar per 30 feet, that gave a difference of 10 millibars – exactly what the device was reporting …

        Reply
  17. Very good project, Rui and Sara, like everyone else that you develop and share.
    Some time ago I developed a weather station and used your project to help me.
    Everything works very well, but I have a question about the BMP280 sensor that maybe you can help me.
    My problem is with atmospheric pressure. In my region, the pressure is around 1009hPa to 1023hPa however, the sensor readings never exceed 906hPa. Have you had this problem?
    Thank you and congratulations for the magnificent projects.

    Reply
  18. Hi Sara, should be possible to have the “Daylight Offset” automatically ie. in Italy, get on last October sunday and get off last March Sunday every year. As far as I know, I tried as follows and it works but the sketch is completely different than yours.
    .
    .
    TimeChangeRule usEDT = {“EDT”, Last, Sun, Mar, 2, +60};
    TimeChangeRule usEST = {“EST”, Last, Sun, Oct, 3, 0};
    Timezone usEastern(usEDT, usEST);
    local = usEastern.toLocal(utc);

    .
    .
    .

    Reply
  19. Hai , Rui it’s a very nice project!Respect! I have problem with compiling scetch-invalid conversion from ‘const char‘ to ‘char‘ [-fpermissive]. You may help me.Thank You!

    Reply
    • Hi.
      Make sure you have an ESP32 selected when compiling the code.
      In which part of the code do you get the error?
      Regards,
      Sara

      Reply
      • Hi! Yes, I’m sure. ESP32 Dev Module. I try with DOIT ESP32 Devkit V1 problem is the same.Thank You for respond!Regards,Rumen! Arduino: 1.8.9 (Windows Store 1.8.21.0) (Windows 10), Board: “DOIT ESP32 DEVKIT V1, 80MHz, 921600, None”

        Weather_Station_New:403:28: error: invalid conversion from ‘const char‘ to ‘char‘ [-fpermissive]

        WiFi.begin(ssid, password);

        ^

        In file included from C:\Users\RR\OneDrive\Documents\Arduino\Weather_Station_New\Weather_Station_New.ino:21:0:

        C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.21.0_x86__mdqgnx93n4wtt\libraries\WiFi\src/WiFi.h:79:9: note: initializing argument 1 of ‘int WiFiClass::begin(char*, const char*)’

        int begin(char* ssid, const char *passphrase);

        ^

        Using library Adafruit_NeoPixel-master at version 1.7.0 in folder: C:\Users\RR\OneDrive\Documents\Arduino\libraries\Adafruit_NeoPixel-master
        Using library Wire at version 1.0.1 in folder: C:\Users\RR\OneDrive\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.4\libraries\Wire
        Using library Adafruit_GFX_Library at version 1.4.2 in folder: C:\Users\RR\OneDrive\Documents\Arduino\libraries\Adafruit_GFX_Library
        Using library Adafruit_SSD1306-master at version 2.4.1 in folder: C:\Users\RR\OneDrive\Documents\Arduino\libraries\Adafruit_SSD1306-master
        Using library SPI at version 1.0 in folder: C:\Users\RR\OneDrive\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.4\libraries\SPI
        Using library Adafruit_Sensor-master at version 1.1.4 in folder: C:\Users\RR\OneDrive\Documents\Arduino\libraries\Adafruit_Sensor-master
        Using library Adafruit_BME280_Library-master at version 2.1.2 in folder: C:\Users\RR\OneDrive\Documents\Arduino\libraries\Adafruit_BME280_Library-master
        Using library WiFi at version 1.2.7 in folder: C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.21.0_x86__mdqgnx93n4wtt\libraries\WiFi
        exit status 1
        invalid conversion from ‘const char‘ to ‘char‘ [-fpermissive]

        Reply
  20. Great project – thanks
    One Question: Which Voltages you are using? USB-connection for the esp32 of course. Additional voltage (5 V) for the leds?

    Walter

    Reply
  21. Thanks for another interesting project. It meets my need for such a device. I really appreciate the professional erudition of Rui Santos. I learned a lot by studying his projects and publications. The description of his projects is very detailed and clear.
    Thank you and I look forward to more projects.

    Reply
  22. Hello Sara and Rui, thank you very much for your always interesting projects, which also give me food for my own projects. In most projects that ask the time from an NTP server, the time zone is corrected by adding an offset. However, it would be simpler and more general to use
    configTzTime (TIME_ZONE, ntpPool) with
    constexpr char TIME_ZONE [] = “MEZ-1MESZ-2, M3.5.0 / 02: 00: 00, M10.5.0 / 03: 00: 00”;
    or as another example
    constexpr char TZ_INFO1 [] = “EST5EDT4, M3.2.0 / 02: 00: 00, M11.1.0 / 02: 00: 00”;
    Best regards

    Reply
  23. This is a great project for an old timer who still gets things to work. Can be adapted to work with components and sensors on hand. Your projects and documentation are always good.

    Reply
  24. All fine as usual.
    But I have a problem.
    When I print temperature with the expression <Serial.println(temperature);> i get the right temperature.
    While I use I get always <1.00>
    Why?
    Renzo

    Reply
  25. Sorry, part of message is always missing, I try again.

    All fine as usual.
    But I have a problem.
    When I print temperature with the expression <Serial.println(temperature);> I get the right temperature.
    While I use , I get always <1.00>
    Why?
    Renzo

    Reply
    • Hi.
      I think that display uses SPI communication, right?
      So, you need to modify the PCB to include the SPI connections.
      If you had any other module that communicates via I2C, you could use the I2C connection for the OLED display to connect any other module.
      Regards,
      Sara

      Reply
      • Thanks for that Sara. So to use one of the full colour TFT displays that I already have, I will need to include the TFT and SPI libraries, define the additional pins cs, dc and rst and make calls to the TFT library instead when I want to display stuff, basically per your TFT tutorial, right ?

        I don’t suppose you have any plans to incorporate a colour TFT display do you ? As they are so cheap and versatile, seems a potentially ‘nice’ way to improve and customise the way that the weather data is displayed. I’m also thinking that a big Nextion display would sit nicely on the end of this project.

        As always, thanks for another excellent project. I have the basic set up built and the current code loaded and running

        Reply
        • Hi Geoff, you are right; me too using the the full colour TFT displays; it’s very easy to have it on top of oled that Rui and Sara mainly used.
          If you wish some details about this don’t hesitate to ask, thanks.

          Reply
          • Thanks Domenico – your reply is greatly appreciated. If you have already adapted the code / hardware to support a colour TFT display, I would be very interested to see how you have done this. I’m hoping as a project for the next couple of months, to add inputs from an anemometer, wind direction vane, and rain sensor. If you want to send anything to me, use [email protected]

          • Thanks Sara. I think it would be a nice addition to this project to be able to use full colour for the display pages and graphics. I have a few additions that I have in mind including adding the ability to read remote sensors to allow outdoor inputs such as an anemometer for windspeed and a vane for wind direction. I think this project has real potential to be one of your best yet. I think your Arduino TFT tutorial will be good enough to get one to work with the ESP development board as the host instead of an actual Arduino board, but if you do find the time to figure it out as a development of the project, that would be awesome. Thanks again for this project and all the good work on other projects, a number of which I have followed and built successfully !

  26. To answer to Geoff to connect a TFT 1.8 to ESP8266:

    This is about from the hardware side:
    Connection TFT 1.8 inches ST7735 driver to ESP8266

    ESP8266 TFT 1.8 (SPI)
    ======================================
    + 5V Pin 06 (RESET)
    GPIO2 Pin 07 (DC)
    GPIO13 (HSPID) Pin 08 (SDA)
    GPIO14 (HSPICLK) Pin 09 (SCK)
    GPIO15 (HSPICS) Pin 10 (CS)

    This is about from the code side:

    #include <Adafruit_GFX.h>
    #include <Adafruit_ST7735.h>
    .
    .

    #define TFT_PIN_CS 15
    #define TFT_PIN_DC 2
    .
    .
    Adafruit_ST7735 tft = Adafruit_ST7735(TFT_PIN_CS, TFT_PIN_DC); // Display-Setup
    .
    .
    tft.initR(INITR_BLACKTAB); // ST7735-Chip initialization

    tft.fillScreen(ST7735_BLACK);
    tft.setRotation(0);

    tft.setTextColor(ST7735_YELLOW);
    tft.setCursor(0, 5);
    tft.setTextSize(2);
    tft.print(“TFT HDOMOTICA”);
    .
    .
    .

    Reply
  27. Great Project, thanks. PCBWay charging me $37+ for the PCB as its “slightly over the size for the $5-10 charge”. Anyone out there got the PCB skills to make this a little smaller? 🙂 I would love to build this, or has anyone got a spare one I can buy from them 🙂

    Reply
  28. no matching fuction for call to TwoWire ::TwoWire(int)’
    I am getting this error as I am a beginner I am sorry I don’t what to do.
    Thanks in advance.Really great stuff enjoying when things work.
    John

    Reply
    • Hi John.
      Make sure you have an ESP32 board selected in Tools > Board, before compiling and uploading the code.
      Regards,
      Sara

      Reply
      • TwoWire I2Cdisplay = TwoWire(1);
        THANK YOU thank you I am 74 and trying to do some of the projects find most ok now I will make sure I have the right board selected.
        John UK

        Reply
    • Hi John

      I’m sure the answer to your problem will be as Sara says below. I have had this message in various forms many times with new projects, and it has always been because the wrong board type is selected in “Tools”. In your excitement to compile, load and see results, it’s very easy to forget to check the setings are right for the board type that you are hoping to compile for … 🙂

      Reply
  29. Hi All,
    I am having trouble with the ‘weather sketch’. The first time through every thing is as i
    would expect. Next time and all subsequent times the temperature readout is NAN
    (Not A Number) reading ,then the next humidity reading is OK on the top of the screen and
    the temperature is OK in small print at the bottom of the screen.
    I need help.
    Ross.

    Reply
  30. I have a BMP280 and AHT20 combo board, im struggling to modify the code so that it takes Temperature and Pressure from BMP and Humidity from AHT

    It works but OLED isnt working, mine has 0x3C Address eventhough it is 128 x 64. i cant find where to change the OLED Address in Program

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.