Wireless Weather Station
From: https://howtomechatronics.com/tutorials/arduino/arduino-wireless-weather-station-project
Arduino Wireless Weather Station Project
In this tutorial we will learn how to make an Arduino based Wireless Weather
Station. You can watch the following video or read the written tutorial
below.
Overview
The outdoor temperature and humidity are measured using the DHT22 sensor and
this data is wirelessly sent to the indoor unit using the NRF24L01
transceiver modules. At the indoor unit, there is also another DHT22 sensor
for measuring the indoor temperature and humidity, as well as a DS3231 Real
Time Clock module which can keep track of the time even if the Arduino loses
power. All of these data is printed on a 0.96” OLED display.
Arduino Wireless Weather Station Circuit Diagram
Let’s take a look at the circuit diagram and how this project works. Note
that I already have detailed tutorials on how each of these modules work, so
for more details you can check them: NRF24L01 Tutorial, DHT22 Tutorial,
DS3231 Tutorial .
Arduino Wireless Weather Station Circuit Diagram
You can get the components needed for this project from the links below:
NRF24L01 Transceiver Module………. Amazon / Banggood / AliExpress
DHT22 Sensor…………….…………………… Amazon / Banggood /
AliExpress
DS3231 Real Time Clock………………….. Amazon / Banggood /
AliExpress
Arduino Nano ………………………….…….. Amazon / Banggood /
AliExpress
Disclosure: These are affiliate links. As an Amazon Associate I earn from
qualifying purchases.
Both the real time clock module and the OLED display use the I2C protocol
for communication with the Arduino so they are connected to the I2C pins or
the analog pins number 4 and 5 on the Arduino Nano board. Right next to the
NRF24L01 transceiver module there is a capacitor to keep the power supply to
it more stable. There is also a pull-up resistor connected to the DHT22 data
pin in order the sensor to work properly.
Ezoic
As for the power supply I used 12V DC power adapter for the indoor unit, and
on the other side, for powering the outdoor unit I used two Li-on batteries
producing around 7.5V. With this configuration the outdoor unit could run
for around 10 days before the batteries discharge, because we transmit data
periodically and in the meantime we put the Arduino to sleep mode, where the
power consumption is only around 7mA.
Custom design PCB
In order to keep the electronics components organized, according to the
circuit diagram I designed a custom PCB using the EasyEDA free online
circuit design software. We can note that the same PCB can be used for both
the indoor and the outdoor unit, only the Arduino board should be programmed
differently.
Arduino Weather Station custom PCB design
Once we finish the design here, we can simply export the Gerber file which
is used for manufacturing the PCB. You can check the EasyEDA project files
of Arduino wireless weather station here.
Then we can order our PCB from JLCPCB, which is actually the sponsor of this
video.
Here we can simply drag and drop the Gerber file and once uploaded we can
review our PCB in the Gerber viewer. If everything is all right then we can
go on, select the properties that we want for our PCB, and then we can order
our PCB at a reasonable price. Note that if it’s your first order from
JLCPCB, you can get up to 10 PCBs for only $2.
Nevertheless, after several days the PCBs have arrived. The quality of the
PCBs is great and everything is exactly the same as in the design.
I started assembling the electronics components of this project by soldering
pin headers on to the PCB. In this way we can easily connect and disconnect
the components when needed.
Then I also inserted and soldered the capacitor and the pull-up resistor.
With this step done, now we can simply attach the components onto the pin
headers of the PCB.
Next, I moved on with making the cases for the project. For that purpose I
used 8mm tick MDF board and using a circular saw, I cut all pieces to size.
In order to have accurate temperature and humidity measurements the sides of
the cases have to allow air to enter the case. So, using a drill and a rasp
I made several slots on the side panels on both the indoor and outdoor
units.
I also made a slot for the OLED display on the front panel, as well as, cut
a small piece of aluminum to size which I will later attach on the front
panel as a decoration.
For assembling the cases I used a wood glue and some clamps, as well as some
screws.
I painted the cases using a spray paint. I used white paint for the outdoor
unit and black one for the indoor unit. After the paint dried out, I simply
inserted the PCBs into the cases.
At the back side of the indoor unit I inserted a power jack and a power
switch, and at the outdoor unit I used a simple jumper wire as a power
switch.
And that’s it, our Arduino wireless weather station is now working, but
what’s left in this video is to take a look how the program works.
Arduino Wireless Weather Station Code
Arduino weather station outdoor unit code:
/*H*******************************************************
* Arduino Wireless Communication Tutorial
Outdoor unit - Transmitter
by Dejan Nedelkovski, www.HowToMechatronics.com
Libraries:
NRF24L01 - TMRh20/RF24, https://github.com/tmrh20/RF24/
DHT22 - DHTlib, https://github.com/RobTillaart/Ardui
no/tree/master/libraries/DHTlib
LowPower - https://github.com/rocketscream/Low-Power
*********************************************************/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <dht.h>
#include <LowPower.h>
// **************** DEFINES ****************************
#define dataPin 8 // DHT22 data pin
// **************** PROTOTYPES *************************
// **************** VARIABLES **************************
dht DHT; // Creates a DHT object
RF24 radio(10, 9); // CE, CSN
const byte address[6] = "00001";
char thChar[32] = "";
String thString = "";
/*F*******************************************************
*
*********************************************************/
void
setup()
{
radio.begin();
radio.openWritingPipe( address );
radio.setPALevel( RF24_PA_MIN );
radio.stopListening();
}
/*F*******************************************************
*
*********************************************************/
void
loop()
{
int readData = DHT.read22( dataPin ); // READS DATA FROM SENSOR
int t = DHT.temperature; // GETS VALUES OF TEMPERATURE
int h = DHT.humidity; // GETS VALUES OF HUMIDITY
thString = String( t ) + String( h );
thString.toCharArray( thChar, 12 );
for( int i = 0; i <= 3; i++ ) // SENT DATA WIRELESSLY TO INDOOR UNIT
{ // SEND DATA 3 TIMES
radio.write( &thChar, sizeof( thChar ) );
delay( 50 );
}
// Sleep for 2 minutes, 15*8 = 120s
for( int sleepCounter = 15; sleepCounter > 0; sleepCounter--)
{
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
}
}
Code language: Arduino (arduino)
Description:
The outdoor unit is the transmitter of the wireless
communication, so here first we need to include the RF24 library, the DHT
library, as well as the LowPower library which is used for putting the
Arduino to sleep mode.
After defining their instances, the pins to which the modules are connected
and some variables, in the setup section we need to initialize the wireless
communication address. Then in the loop section, first we read the data from
the DHT22 sensor or that’s the temperature and humidity. Initially these
values are integers and separated, so I convert them into a single String
variable, put them in the character array, and using the radio.write()
function, sent this data to the indoor unit. Using the for loop we send the
data 3 times in order to be sure that the receiver will get data in case the
controller is busy at the time of sending.
At the end we put the Arduino to sleep mode for a particular period of time
in order to minimize the power consumption.
/*H*******************************************************
* Arduino weather station indoor unit code:
Arduino Wireless Communication Tutorial
Indoor unit - Receiver
by Dejan Nedelkovski, www.HowToMechatronics.com
Libraries:
DS3231 - http://www.rinkydinkelectronics.com/library.php?id=73
U8G2 - https://github.com/olikraus/u8g2
*********************************************************/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <dht.h>
#include <DS3231.h>
#include <U8g2lib.h>
#include <Wire.h>
// **************** DEFINES ****************************
#define dataPin 8 // DHT22 sensor
#define Temperature_20Icon_width 27
#define Temperature_20Icon_height 47
static const unsigned char Temperature_20Icon_bits[] U8X8_PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00,
0xc0, 0xe1, 0x00, 0x00, 0xe0, 0xc0, 0x01, 0x00, 0x60, 0x80, 0xf9, 0x03,
0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x79, 0x00,
0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0xf9, 0x03,
0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x8c, 0x79, 0x00,
0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0xf9, 0x03,
0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x79, 0x00,
0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0xf9, 0x03,
0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00,
0x70, 0x9e, 0x03, 0x00, 0x38, 0x1e, 0x07, 0x00, 0x18, 0x3e, 0x0e, 0x00,
0x1c, 0x3f, 0x0c, 0x00, 0x0c, 0x7f, 0x18, 0x00, 0x8c, 0xff, 0x18, 0x00,
0x8e, 0xff, 0x38, 0x00, 0xc6, 0xff, 0x31, 0x00, 0xc6, 0xff, 0x31, 0x00,
0xc6, 0xff, 0x31, 0x00, 0x8e, 0xff, 0x38, 0x00, 0x8c, 0xff, 0x18, 0x00,
0x0c, 0x7f, 0x1c, 0x00, 0x3c, 0x1c, 0x0e, 0x00, 0x78, 0x00, 0x06, 0x00,
0xe0, 0x80, 0x07, 0x00, 0xe0, 0xff, 0x03, 0x00, 0x80, 0xff, 0x00, 0x00,
0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#define Humidity_20Icon_width 27
#define Humidity_20Icon_height 47
static const unsigned char Humidity_20Icon_bits[] U8X8_PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00,
0x00, 0x70, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00,
0x00, 0xdc, 0x01, 0x00, 0x00, 0x8e, 0x01, 0x00, 0x00, 0x86, 0x03, 0x00,
0x00, 0x06, 0x03, 0x00, 0x00, 0x03, 0x07, 0x00, 0x80, 0x03, 0x06, 0x00,
0x80, 0x01, 0x0c, 0x00, 0xc0, 0x01, 0x1c, 0x00, 0xc0, 0x00, 0x18, 0x00,
0xe0, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0x70, 0x00, 0x70, 0x00,
0x30, 0x00, 0xe0, 0x00, 0x38, 0x00, 0xc0, 0x00, 0x18, 0x00, 0xc0, 0x01,
0x1c, 0x00, 0x80, 0x01, 0x0c, 0x00, 0x80, 0x03, 0x0e, 0x00, 0x80, 0x03,
0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x07,
0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06,
0x63, 0x00, 0x00, 0x06, 0x63, 0x00, 0x00, 0x06, 0x63, 0x00, 0x00, 0x06,
0xe3, 0x00, 0x00, 0x06, 0xc7, 0x00, 0x00, 0x06, 0xc6, 0x01, 0x00, 0x07,
0x86, 0x03, 0x00, 0x03, 0x0e, 0x1f, 0x00, 0x03, 0x0e, 0x1e, 0x80, 0x01,
0x1c, 0x00, 0xc0, 0x01, 0x38, 0x00, 0xe0, 0x00, 0x78, 0x00, 0x70, 0x00,
0xf0, 0x00, 0x38, 0x00, 0xe0, 0x07, 0x1f, 0x00, 0x80, 0xff, 0x0f, 0x00,
0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00
};
// **************** PROTOTYPES *************************
void drawDate();
void drawInTemperature();
void drawInHumidity();
void drawOutTemperature();
void drawOutHumidity();
// **************** VARIABLES **************************
dht DHT; // Creats DHT object
DS3231 rtc( SDA, SCL );
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2( U8G2_R0, U8X8_PIN_NONE);// RESET=
RF24 radio(10, 9); // CE, CSN
const byte address[6] = "00001";
char text[6] = "";
int readDHT22, t, h;
String inTemp, inHum, outTemp, outHum;
String rtcTime, rtcDate;
int draw_state = 0;
unsigned long previousMillis = 0;
long interval = 3000;
/*F*******************************************************
*
*********************************************************/
void
setup()
{
radio.begin();
radio.openReadingPipe( 0, address );
radio.setPALevel( RF24_PA_MIN );
radio.startListening();
u8g2.begin();
rtc.begin();
}
/*F*******************************************************
*
*********************************************************/
void
loop()
{
if( radio.available())
{
radio.read( &text, sizeof( text ) ); // READ INCOMING DATA
// OUTDOOR Temp
outTemp = String(text[0]) + String(text[1]) + char(176) + "C";
outHum = String(text[2]) + String(text[3]) + "%"; // Outdoor Humidity
}
unsigned long currentMillis = millis();
if( currentMillis - previousMillis > interval)
{
previousMillis = currentMillis;
u8g2.firstPage();
do
{
switch( draw_state )
{
case 0:
drawDate();
break;
case 1:
drawInTemperature();
break;
case 2:
drawInHumidity();
break;
case 3:
drawOutTemperature();
break;
case 4:
drawOutHumidity();
break;
}
}while( u8g2.nextPage() );
draw_state++;
if( draw_state > 4)
{
draw_state = 0;
}
}
}
/*F*******************************************************
*
*********************************************************/
void
drawDate()
{
String dowa = rtc.getDOWStr();
dowa.remove( 3 );
rtcDate = dowa + " " + rtc.getDateStr();
u8g2.setFont( u8g2_font_timB14_tr );
u8g2.setCursor( 0, 15 );
rtcTime = rtc.getTimeStr(); // DS3231 RTC TIME
rtcTime.remove( 5 );
u8g2.print( rtcDate );
u8g2.setFont( u8g2_font_fub30_tf );
u8g2.setCursor( 8, 58 );
u8g2.print( rtcTime );
}
/*F*******************************************************
*
*********************************************************/
void
drawInTemperature()
{
readDHT22 = DHT.read22( dataPin ); // READS DATA FROM SENSOR
t = DHT.temperature; // GETS VALUES OF TEMPERATURE
inTemp = String(t) + char(176) + "C";
u8g2.setFont( u8g2_font_helvR14_tr );
u8g2.setCursor( 24, 15 );
u8g2.print( "INDOOR" );
u8g2.setFont( u8g2_font_fub30_tf );
u8g2.setCursor( 36, 58 );
u8g2.print( inTemp );
u8g2.drawXBMP( 0, 17, Temperature_20Icon_width, Temperature_20Icon_height
, Temperature_20Icon_bits);
}
/*F*******************************************************
*
*********************************************************/
void
drawInHumidity()
{
h = DHT.humidity; // GETS VALUES OF HUMIDITY
inHum = String( h ) + "%";
u8g2.setFont( u8g2_font_helvR14_tr );
u8g2.setCursor( 24, 15 );
u8g2.print( "INDOOR" );
u8g2.setFont( u8g2_font_fub30_tf );
u8g2.setCursor( 36, 58 );
u8g2.print( inHum );
u8g2.drawXBMP( 0, 17, Humidity_20Icon_width, Humidity_20Icon_height
, Humidity_20Icon_bits);
}
/*F*******************************************************
*
*********************************************************/
void
drawOutTemperature()
{
u8g2.setFont( u8g2_font_helvR14_tr );
u8g2.setCursor( 12, 15 );
u8g2.print( "OUTDOOR" );
u8g2.setFont( u8g2_font_fub30_tf );
u8g2.setCursor( 36, 58 );
u8g2.print( outTemp );
u8g2.drawXBMP( 0, 17, Temperature_20Icon_width, Temperature_20Icon_height
, Temperature_20Icon_bits);
}
/*F*******************************************************
*
*********************************************************/
void
drawOutHumidity()
{
u8g2.setFont( u8g2_font_helvR14_tr );
u8g2.setCursor( 12, 15 );
u8g2.print( "OUTDOOR" );
u8g2.setFont( u8g2_font_fub30_tf );
u8g2.setCursor( 36, 58 );
u8g2.print( outHum );
u8g2.drawXBMP( 0, 17, Humidity_20Icon_width, Humidity_20Icon_height
, Humidity_20Icon_bits);
}
Description:
On the other side, at the indoor unit or the receiver, we need
to include two more libraries, one for the DS3231 real time clock module and
one for the OLED display, the U8G2 library. In the same way as previously,
we need to define the instances, the pins and some variables need for the
program below. Also here we need to define the temperature and humidity
icons as bitmaps.
Temperature icon bitmap:
#define Temperature_20Icon_width 27
#define Temperature_20Icon_height 47
static const unsigned char Temperature_20Icon_bits[] U8X8_PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00,
0xc0, 0xe1, 0x00, 0x00, 0xe0, 0xc0, 0x01, 0x00, 0x60, 0x80, 0xf9, 0x03,
0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x79, 0x00,
0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0xf9, 0x03,
0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x8c, 0x79, 0x00,
0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0xf9, 0x03,
0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x79, 0x00,
0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0xf9, 0x03,
0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00,
0x70, 0x9e, 0x03, 0x00, 0x38, 0x1e, 0x07, 0x00, 0x18, 0x3e, 0x0e, 0x00,
0x1c, 0x3f, 0x0c, 0x00, 0x0c, 0x7f, 0x18, 0x00, 0x8c, 0xff, 0x18, 0x00,
0x8e, 0xff, 0x38, 0x00, 0xc6, 0xff, 0x31, 0x00, 0xc6, 0xff, 0x31, 0x00,
0xc6, 0xff, 0x31, 0x00, 0x8e, 0xff, 0x38, 0x00, 0x8c, 0xff, 0x18, 0x00,
0x0c, 0x7f, 0x1c, 0x00, 0x3c, 0x1c, 0x0e, 0x00, 0x78, 0x00, 0x06, 0x00,
0xe0, 0x80, 0x07, 0x00, 0xe0, 0xff, 0x03, 0x00, 0x80, 0xff, 0x00, 0x00,
0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
Code language: Arduino (arduino)
For this purpose we can use GIMP, an open source image editor, through which
we can draw anything and then export it as bitmap(.xbm).
Then we can open this file using a notepad and from there we can copy the
bitmap into the Arduino code.
Note that here we can define the bitmap as constant using the PROGMEM
variable modifier, and it that way the bitmap will be stored in the flash
memory instead of the SRAM of the Arduino board.
static const unsigned char Temperature_20Icon_bits[] U8X8_PROGMEM // Save in
the Flash memory
static unsigned char Temperature_20Icon_bits[] // Save in the SRAM
In the setup section we need to initialize the wireless communication as
well as initialize the OLED display and the real time clock module.
Then in the loop section we constantly check whether there is an incoming
data available to be read via the NRF24L01 modules. If true, using the
radio.read() function we read it, and store the first two characters into
the temperature String variable, and the next two characters into the
humidity String variable.
Then we use the millis() function in order to display the various data on
the display at intervals defined with the interval variable which I set it
to 3 seconds. We are using the millis() function because in this way the
rest of the code can be repeatedly executed, while in case we use the
delay() function, the program waits for that period so in that way we would
probably miss the incoming data from the outdoor unit.
Next, using the firstPage() and nextPage() function of the U8G2 library we
print the five different screens which are defined with the custom
functions.
ADVERTISING
Ezoic
The drawDate() custom function gets the date and time information from the
real time clock module and prints it on the display appropriately. The
drawInTemperature() function reads the indoor temperature and prints it
appropriately on the display. In fact, the same method is used for printing
all of the screens on the display.
So that would be all, I hope you enjoyed this Arduino project and learned
something new. Feel free to ask any question in the comments section below