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.
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):
- ESP32 Code (Arduino IDE)
- Gerber files
- EasyEDA project to edit the PCB
- Click here to download all the files
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).
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:
Component | ESP32 Pin Assignment |
BME280 | GPIO 21 (SDA), GPIO 22 (SCL) |
OLED Display | GPIO 21 (SDA), GPIO 22 (SCL) |
Light Dependent Resistor (LDR) | GPIO 33 |
Pushbutton | GPIO 18 |
Addressable RGB LEDs | GPIO 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:
- Current date and time;
- Temperature
- Humidity
- Pressure
- 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.
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):
- DOIT ESP32 DEVKIT V1 Board – read Best ESP32 Development Boards
- BME280 (4 pins)
- I2C OLED Display (4 pins)
- Light dependent resistor
- Pushbutton
- 2x 10k Ohm resistor
- Breadboard
- Jumper wires
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:
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.
Having the parts assigned, place each component. When you’re happy with the layout, make all the connections and route your PCB.
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.
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.
3. PCBWay can grab all the PCB details and automatically fills them for you. Use the “Quick-order PCB (Autofill parameters)”.
4. Press the “+ Add Gerber file” button to upload the provided Gerber files.
And that’s it. You can also use the OnlineGerberViewer to check if your PCB is looking as it should.
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.
You can increase your PCB order quantity and change the solder mask color. I’ve ordered the Blue color.
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.
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:
- ESP32-CAM with Telegram: Take Photos, Control Outputs, Request Sensor Readings and Motion Notifications
- ESP32 IoT Shield PCB with Dashboard for Outputs and Sensors
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:
- DOIT ESP32 DEVKIT V1 Board (36 GPIOs)
- 12x SMD WS2812B addressable RGB LEDs
- 2x 10k Ohm SMD resistor (1206)
- 12x 10nF capacitors (0805)
- Pushbutton (0.55 mm)
- Female pin header socket (2.54 mm)
- BME280 (4 pins)
- Light dependent resistor
- I2C SSD1306 0.96inch OLED display (4 pins)
Here’s the soldering tools I’ve used:
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.
The ESP32 board should stack perfectly on the header pins on the other side of the PCB.
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();
}
}
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:
- ESP32 NTP Client-Server: Get Date and Time (Arduino IDE)
- ESP32 OLED Display with Arduino IDE
- ESP32/ESP8266: DHT Temperature and Humidity Readings in OLED Display
- ESP32 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity)
- Guide for WS2812B Addressable RGB LED Strip with Arduino
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.
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:
- ESP32-CAM with Telegram: Take Photos, Control Outputs, Request Sensor Readings and Motion Notifications
- ESP32 IoT Shield PCB with Dashboard for Outputs and Sensors
- Build an All-in-One ESP32 Weather Station Shield
- Build a Multisensor Shield for ESP8266
- EXTREME POWER SAVING with Microcontroller External Wake Up: Latching Power PCB
Learn more about the ESP32 with our resources:
- Learn ESP32 with Arduino IDE (eBook + video course)
- MicroPython Programming with Arduino IDE
- More ESP32 tutorials and projects…
[Update] the bare PCB giveaway ended and the winners are: Etienne Bogaert, Wal Taylor, Charles Geiser, JM GIGAN and Kristian C.
It is a more exciting project to make during christmas holidays. Thankyou again.
It is an exciting and very interesting project especially when monitoring data gathered from each sensor in real time.
Thank you very much.
Nice project!
Just going into lockdown until 00:00 on January 1st – guess what I’ll be doing.
Thanks for all your tutorials.
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
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
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
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.
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…?
Yes, Great project!
Probably would be a goood project for my son bedroom.
Cool!
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.
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.
Hi.
You can use our project as long as you reference to the original source.
I hope your students like the project.
Regards,
Sara
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”!
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
I’m very inspired by this project and just ordered my first ESP32 to make my version 🙂
Thanks a lot for the tutorial!
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
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
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
Brilliant! Yet another great tutorial that introduces a new skill – this time PCB development. Thanks so much!
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
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
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?
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).
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 …
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.
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);
.
.
.
Looks like a great update to my monochrome Squix weather station!
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!
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
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]
Hi Rui and Sara! I resolve my problem,just pre-install the boards ESP32 and ESP8266. Regards,Rumen
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
Hi.
We’re only using USB connection for the ESP32.
Regards,
Sara
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.
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
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.
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
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
The part of the message missing is this
float temperature = (bme.readTemperature());
Serial.println(temperature);
Would it be possible to use one of the 1.8″ colour TFT displays with this project ?
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
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
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.
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]
Hi again.
Yes, that’s right.
Those are the modifications you might need to do.
Unfortunately, at the moment we don’t have any tutorial about a TFT display.
I have this one for the Arduino, but I think it might be outdated: https://randomnerdtutorials.com/guide-to-1-8-tft-display-with-arduino/
Will add TFT display with ESP32 to my to-do list.
Regards,
Sara
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 !
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”);
.
.
.
Thanks Domenico. Got your email. Will be back to you shortly
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 🙂
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
Hi John.
Make sure you have an ESP32 board selected in Tools > Board, before compiling and uploading the code.
Regards,
Sara
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
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 … 🙂
Yes, Great project!
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.
Someone posted a fix for this a couple of weeks ago. Involves an extra couple of lines of code. Worked fine for me. If you go back up the project comments, I’m sure you’ll find it
Thanks Geoff
I think that was tried but will have another go as soon as i can.
Ross.
Thanks Jeoff,
I didn’t put the two lines in right place last time.
Works now.
Ross.
Glad you’ve resolved it !
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
Hi.
In our program we’re also using that address.
If yours is different you can change it in this line
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
If you’re not sure about the address of your display, you can run an I2C scanner sketch: https://randomnerdtutorials.com/esp32-i2c-scanner-arduino/
If you’re struggling with using multiple I2C devices simultaneously, you can take a look at our I2C guide: https://randomnerdtutorials.com/esp32-i2c-communication-arduino-ide/
I hope this helps.
Regards,
Sara