Introduction: RPi - IoT Weather Station

About: Engineer, writer and forever student. Passionate to share knowledge of electronics with focus on IoT and robotics.

On this tutorial we will get data from several different sensors, sending them to an IoT service, and to a mobile App (Thingsview), where we can log and play with data. We will explore several different communication ways of connecting sensors to a Raspberry Pi, as:

  • DHT22 - Temperature & Humidity Sensor - Digital Comm
  • DS18B20 - Temperature Sensor - 1-Wire
  • BMP180 - Temperature & Pressure Sensor - I2C
  • UV - Ultra Violet Sensor - Analog Sensor via A/D and SPI bus

In short, all data will be captured, saved locally on a CSV file and send to an IoT service (, via MQTT protocol, as you can see on below block diagram:

To complete a real Weather Station, on the final step, you will also learn how to measure wind speed and direction, following Mauricio Pinto's tutorial.


Step 1: Development Environment - Jupyter Notebook

Jupyter Notebook is a fantastic tool, or better, an open-source web application that allows you to create and share documents that contain live code, equations, visualizations, and narrative text. Jupyter notebook is largely used in Data Science, for cleaning and transforming data, doing numerical simulation, statistical modeling, data visualization, machine learning, and much more!

On this tutorial, we will use Jupyter Notebook to interact with Raspberry Pi GPIOs, directly reading sensors and sending data to the internet.


You may skip this step if already have Jupyter installed on your RPi

To install Jupyter on your Raspberry (that will run with Python 3), open Terminal and enter with following commands:

sudo pip3 install jupyter

sudo ipython3 kernelspec install-self

Now on your terminal, run the command:

jupyter notebook

And that's it!!!! Amazing! very simple and easy. The Jupyter Notebook will be running as a server on:


Note that your default browser will be automatically opened on the above address, running as a "Home Page", as shown at above pictures.

To stop the server and close the "kernels" (the Jupyter notebooks), you must use [Ctrl] + [C] from your keyboard.

From now one, any time that you start your Pi and want to use Jupyter Notebook, just type the command: "Jupyter notebook" on your terminal and keep it running all the time. This is very important!

If you need to use the terminal for another task as run a program, for example, open a new Terminal window.

You can follow this tutorial step by step, creating your own Notebook, or download the final one from my GitHub: Rpi_Weather_Station.ipynb.

Step 2: DHT22 - Temperature & Humidity Sensor

The first sensor to be installed will be the DHT22 for capturing air temperature and relative humidity data. The ADAFRUIT site provides great information about those sensors. Bellow, some information retrieved from there:


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

DHT22 Main characteristics:

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

Once usually you will use the sensor on distances less than 20m, a 4K7 ohm resistor should be connected between Data and VCC pins. The DHT22 output data pin will be connected to Raspberry GPIO 16. Check the above electrical diagram, connecting the sensor to RPi pins as below:

  1. Pin 1 - Vcc ==> 3.3V
  2. Pin 2 - Data ==> GPIO 16
  3. Pin 3 - Not Connect
  4. Pin 4 - Gnd ==> Gnd
Do not forget to Install the 4K7 ohm resistor between Vcc and Data pins

Once the sensor is connected, we must also install its library on our RPi.

Installing DHT Library:

On your Raspberry, starting on /home, go to /Documents

cd Documents 

Create a directory to install the library and move to there:

mkdir DHT22_Sensor
cd DHT22_Sensor

On your browser, go to Adafruit GitHub:

Download the library by clicking the download zip link to the right and unzip the archive on your Raspberry Pi recently created folder. Then go to the directory of the library (subfolder that is automatically created when you unzipped the file), and execute the command:

sudo python3 install

On The Jupyter Notebook, Import Adafrut DHT Library, define the digital pin to connect the DHT with RPi and run the code to capture temperature and humidity:

import Adafruit_DHT
DHT22Sensor = Adafruit_DHT.DHT22
DHTpin = 16

humDHT, tempDHT = Adafruit_DHT.read_retry(DHT22Sensor, DHTpin)
if (humDHT is not None) and (tempDHT is not None):
    hum = round (humDHT,1)
    temp = round (tempDHT, 1)

Run the Cell and print the results:

print('Temperature = {}*C  Humidity = {}%'.format(temp, hum))

Below, the portion of Jupyter Notebook showing the result:

Step 3: DS18B20 - Temperature Sensor

Sensor Overview:

We will use in this tutorial a waterproofed version of the DS18B20 sensor. It is very useful for capturing temperature in wet conditions, for example on humid soil. The sensor is isolated and can take measurements until 125oC (Adafrut does not recommend to use it over 100oC due to its cable PVC jacket).

The DS18B20 is a digital sensor what makes it good to use even over long distances! These 1-wire digital temperature sensors are fairly precise (±0.5°C over much of the range) and can give up to 12 bits of precision from the onboard digital-to-analog converter. They work great with the RPi using a single digital pin, and you can even connect multiple ones to the same pin, each one has a unique 64-bit ID burned in at the factory to differentiate them.

The sensor works from 3.0 to 5.0V, which means that it can be powered directly from the 3.3V provided by one of the Raspberry pins (1 or 17).

The sensor has 3 wires:

  • Black: GND
  • Red: VCC
  • Yellow: 1-Wire Data

Here, you can find the full data: DS18B20 Datasheet

Sensor Installation:

Follow the above diagram and make the connections:

  • Vcc ==> 3.3V
  • Gnd ==> Gnd
  • Data ==> GPIO 4 (default for library)

Installing the Python Library:

Next, let's install the Python library that will handle the sensor:

sudo pip3 install w1thermsensor

Before running the script to test the sensor, check if the "1-Wire" interface is enabled in your RPi (see above print screen)

Do not forget to restart your RPi, after changeing its configuration

Testing the sensor:

For testing the sensor a simple python code can be used:

from w1thermsensor import W1ThermSensor 
ds18b20Sensor = W1ThermSensor() 
tempExt = round(ds18b20Sensor.get_temperature(), 1)
print('External Temperature = {}*C'.format(tempExt))

Below, the portion of Jupyter Notebook showing the result:

Step 4: BMP180 - Temperature & Pressure Sensor

Sensor Overview:

The BMP180 is the successor of the BMP085, a new generation of high precision digital pressure sensors for consumer applications. The ultra-low power, low voltage electronics of the BMP180 is optimized for use in mobile phones, PDAs, GPS navigation devices and outdoor equipment. With a low altitude noise of merely 0.25m at fast conversion time, the BMP180 offers superior performance. The I2C interface allows for easy system integration with a microcontroller. The BMP180 is based on piezo-resistive technology for EMC robustness, high accuracy, and linearity as well as long-term stability.

The complete BMP datasheet can be found here: BMP180 - Digital Pressure Sensor

Sensor Installation:
Follow the above diagram and make the connections:

  • Vin ==> 3.3V
  • GND ==> GND
  • SCL ==> GPIO 3
  • SDA ==> GPIO 2

Enabling I2C Interface

Go to RPi Configuration and confirm that I2C interface is enabled. If not, enable it and restart the RPi.

Using the BMP180

If everything has been installed and connected okay, you are now ready to turn on your Pi and start seeing what the BMP180 is telling you about the world around you.

The first thing to do is to check if the Pi sees your BMP180. Try the following in a terminal window:

sudo i2cdetect -y 1

If the command worked, you should see something similar to the above Terminal Printscreen, showing that the BMP180 is on channel '77'.

Installing the BMP180 Library:

Create a directory to install the library and go there:

mkdir BMP180_Sensor
cd BMP180_Sensor

On your browser, go to Adafruit GITHub:

Download the library by clicking the download zip link to the right and unzip the archive on your Raspberry Pi created folder. Then go to the subfolder created and execute the following command in the directory of the library:

sudo python3 install

On Jupyter, write the following code:

import Adafruit_BMP.BMP085 as BMP085
bmp180Sensor = BMP085.BMP085()
tempBMP = round(bmp180Sensor.read_temperature(), 1)
presBMP = round(bmp180Sensor.read_pressure()/100, 1) # absolute pressure in hPa
altBMP =  round(bmp180Sensor.read_altitude(),1)

Check the variables read by the sensor with bellow code:

print ('Temperature = {} C '.format(tempBMP))
print ('Pressure = {} hPa [or mbar]'.format(presBMP))
print ('Altitud = {} m '.format(altBMP))

Below, the portion of Jupyter Notebook showing the result

Note that the sensor pressure is presented in Pa (Pascals). See next step to better understand about this unit.

Step 5: Measuring Weather and Altitude With BMP180

Let's take a time to understand a little bit more about what we will get, with the BMP readings. You can skip this part of the tutorial, or return later.

If you want to know more about Sensor readings, please go to this great tutorial:

The BMP180 was designed to accurately measure atmospheric pressure. Atmospheric pressure varies with both weather and altitude.

What is Atmospheric Pressure?

The definition of atmospheric pressure is a force that the air around you is exerting on everything. The weight of the gasses in the atmosphere creates atmospheric pressure. A common unit of pressure is "pounds per square inch" or psi. We will use here the international notation, that is newtons per square meter, which are called pascals (Pa).

If you took 1 cm wide column of air would weigh about 1 kg

This weight, pressing down on the footprint of that column, creates the atmospheric pressure that we can measure with sensors like the BMP180. Because that cm-wide column of air weighs about 1Kg, it follows that the average sea level pressure is about 101325 pascals, or better, 1013.25 hPa (1 hPa is also known as milibar - mbar). This will drop by about 4% for every 300 meters you ascend. The higher you get, the less pressure you’ll see, because the column to the top of the atmosphere is that much shorter and therefore weighs less. This is useful to know because by measuring the pressure and doing some math, you can determine your altitude.

The air pressure at 3,810 meters is only half of that at sea level.

The BMP180 outputs absolute pressure in pascals (Pa). One pascal is a very small amount of pressure, approximately the amount that a sheet of paper will exert resting on a table. You will more often see measurements in hectopascals (1 hPa = 100 Pa). The library used here provides outputs floating-point values in hPa, which also happens to equal one millibar (mbar).

Here are some conversions to other pressure units:

  • 1 hPa = 100 Pa = 1 mbar = 0.001 bar
  • 1 hPa = 0.75006168 Torr
  • 1 hPa = 0.01450377 psi (pounds per square inch)
  • 1 hPa = 0.02953337 inHg (inches of mercury)
  • 1 hpa = 0.00098692 atm (standard atmospheres)

Temperature Effects

Because temperature affects the density of a gas, and density affects the mass of a gas, and mass affects the pressure (whew), atmospheric pressure will change dramatically with temperature. Pilots know this as “density altitude”, which makes it easier to take off on a cold day than a hot one because the air is denser and has a greater aerodynamic effect. To compensate for temperature, the BMP180 includes a rather good temperature sensor as well as a pressure sensor.

To perform a pressure reading, you first take a temperature reading, then combine that with a raw pressure reading to come up with a final temperature-compensated pressure measurement. (The library makes all of this very easy.)

Measuring Absolute Pressure

If your application requires measuring absolute pressure, all you have to do is get a temperature reading, then perform a pressure reading (see the example sketch for details). The final pressure reading will be in hPa = mbar. If you wish, you can convert this to a different unit using the above conversion factors.

Note that the absolute pressure of the atmosphere will vary with both your altitude and the current weather patterns, both of which are useful things to measure.

Weather Observations

The atmospheric pressure at any given location on earth (or anywhere with an atmosphere) isn’t constant. The complex interaction between the earth’s spin, axis tilt, and many other factors result in moving areas of higher and lower pressure, which in turn cause the variations in weather we see every day. By watching for changes in pressure, you can predict short-term changes in the weather. For example, dropping pressure usually means wet weather or a storm is approaching (a low-pressure system is moving in). Rising pressure usually means that clear weather is approaching (a high-pressure system is moving through). But remember that atmospheric pressure also varies with altitude. The absolute pressure in my house, Lo Barnechea in Chile (altitude 950m) will always be lower than the absolute pressure in San Francisco for example (less than 2 meters, almost sea level). If weather stations just reported their absolute pressure, it would be difficult to directly compare pressure measurements from one location to another (and large-scale weather predictions depend on measurements from as many stations as possible).

To solve this problem, weather stations always remove the effects of altitude from their reported pressure readings by mathematically adding the equivalent fixed pressure to make it appear as if the reading was taken at sea level. When you do this, a higher reading in San Francisco than Lo Barnechea will always be because of weather patterns, and not because of altitude.

To do this, there is a function in the library called sea level(P,A). This takes the absolute pressure (P) in hPa, and the station’s current altitude (A) in meters, and removes the effects of the altitude from the pressure. You can use the output of this function to directly compare your weather readings to other stations around the world.

Determining Altitude

Since pressure varies with altitude, you can use a pressure sensor to measure altitude (with a few caveats). The average pressure of the atmosphere at sea level is 1013.25 hPa (or mbar). This drops off to zero as you climb towards the vacuum of space. Because the curve of this drop-off is well understood, you can compute the altitude difference between two pressure measurements (p and p0) by using a specific equation.

If you use sea level pressure (1013.25 hPa) as the baseline pressure (p0), the output of the equation will be your current altitude above sea level. There’s a function in the library called altitude(P,P0) that lets you get the "calculated altitude".

The above explanation was extracted from BMP 180 Sparkfun tutorial.

Step 6: Sea Level Pressure Measurement

As we could learn on the previous step, it is important to have on hand the Sea Level pressure, that is calculated once we have the real altitude where we are measuring the absolute pressure. The below function will help us with that:

def bmp180GetData(altitude):

    temp = bmp180Sensor.read_temperature()
    pres = bmp180Sensor.read_pressure()
    alt =  bmp180Sensor.read_altitude()
    presSeaLevel = pres / pow(1.0 - altitude/44330.0, 5.255) 

    temp = round (temp, 1)
    pres = round (pres/100, 2) # absolute pressure in hPa (or mbar)
    alt = round (alt)
    presSeaLevel = round (presSeaLevel/100, 2) # absolute pressure in hPa (or mbar)

    return temp, pres, alt, presSeaLevel

On my case, I have the BMP180 installed on a measured altitude of 957 meters, so we can have the following data from sensors:

Step 7: Using the ADC (Analog to Digital Converter)

On the next step, we will discuss how to get UV data from a very simple sensor. The problem here is that the Raspberry Pi does not have the analog input pins as an Arduino or NodeMCU, but we can overcome this problem by using an analog to digital (A/D) converter which will help in interfacing the analog sensors with the Raspberry Pi. The A/D converter that we will use on this project is the popular MCP3008.

MCP3008 is a 10bit 8-channel ADC (Analog to Digital Converter) which use the SPI bus protocol for interfacing with the Raspberry Pi. It is cheap and doesn’t require any additional components with it. It gives you 8 analog inputs and it uses just four GPIOs of Raspberry Pi, plus power and ground pins. MCP3008 output will be a range from 0-1,023 where 0 means 0V and 1,023 means 3.3V.

MCP3008 Pinout

The pin's numbering of the MCP3008 starts from the top/left, having the half circle on top as you can see in the above pinout diagram. MCP3008 ADC has a total of 16 pins out of which 8 pins are for taking the analog input. The analog input pins are from CH0-CH7 (Pins 1-8). On the other side (pins 9 - 16), we have different pins which are as follows:

  • 09 - DGND is a digital ground pin for the chip.
  • 10 - CS is the chip select pin. Connected to RPi pin 24 (SPI0 - CE)
  • 11 - DIN is the data input pin from the Raspberry Pi pin 19 (SPI0 - MOSI)
  • 12 - DOUT is the data output pin. Connected to RPi pin 21 (SPI0 - MISO)
  • 13 - CLK is the clock pin. Connected to RPi pin 23 (SPI0 - SCLK)
  • 14 - AGND is the analog ground pin.
  • 15 - VREF is the analog reference voltage. Connect to 3.3V. You can change it if you want to change the scale. 16 - VDD is the power pin for the chip.

On this project, we will use Channel 0 (Pin 1) as the analog input.


The Raspberry Pi is equipped with one SPI bus that has 2 chip selects. The SPI master driver is disabled by default on Raspbian. To enable it, use raspi-config to confirm that SPI bus is enabled (the same procedure that was done before with 1-Wire).

As a start, import spidev, a Linux driver to access the SPI bus:

import spidev

And open and configure the bus:

spi = spidev.SpiDev(),0)

From there, you can access any of the analog channels of our ADC. For testing use the below function:

def ReadChannel(channel):
  adc = spi.xfer2([1,(8+channel)<<4,0])
  data = ((adc[1]&3) << 8) + adc[2]
  return data
For testing, connect Channel 0 (MCP3008 pin 1) to 3.3V and run the above function:

As a result, you should see: 1023

Step 8: The Analog UV Sensor

This UV sensor generates an analog output proportional to Ultra-Violet radiation found on the light-sensing spectrum. It uses a UV photodiode (based on Gallium Nitride), which can detect the 240-370nm range of light (which covers UVB and most of UVA spectrum). The signal level from the photodiode is very small, in the nano-ampere level, so the module has embedded an operational amplifier to amplify the signal to a more readable volt-level (0 to 1V).

The sensor and op-amp can be powered, by connecting VCC to 3.3VDC and GND to power ground. The analog signal can be gotten from the OUT pin. Its Output will be in millivolts and will be read by the Analog Input CH0 of ADC connected to our RPi.

Using the same code shown in the last step, we can see the "raw data" generated by our UV sensor (in this case "43"):

Having the raw sensor data, we should "convert" (or " map") it for values to be better handled by the code. We can do it with the function readSensorUV(). This function reads the UV sensor 3 times, taking the average and converting the measured value to mV:

def readSensorUV():                   
    numOfReadings = 3
    dataSensorUV = 0
    for i in range(numOfReadings): 
        dataSensorUV += ReadChannel(0)
    dataSensorUV /= numOfReadings
    dataSensorUV = (dataSensorUV * (3.3 / 1023.0))*1000;
    return round(dataSensorUV)

For example, a raw measurement of "43" is, in fact, equivalent to 128mV:

If we look at the table and curve below:

we will see that 128mV should be related to radiation between index 0 and 1. Let's create a function to calculate this index that is the most common measurement of UV radiation. What we will do is consider a range, having the Vout shown at the above table as the start point, with a range of 110mV. For example, UV measurements between 227mV and 337mv will be considered Index 1.

def indexCalculate(dataSensorUV):
    if dataSensorUV < 227: indexUV = 0
    elif (227 <= dataSensorUV) & (dataSensorUV < 318): indexUV = 1
    elif (318 <= dataSensorUV) & (dataSensorUV < 408): indexUV = 2
    elif (408 <= dataSensorUV) & (dataSensorUV < 503): indexUV = 3
    elif (503 <= dataSensorUV) & (dataSensorUV < 606): indexUV = 4    
    elif (606 <= dataSensorUV) & (dataSensorUV < 696): indexUV = 5
    elif (696 <= dataSensorUV) & (dataSensorUV < 795): indexUV = 6
    elif (795 <= dataSensorUV) & (dataSensorUV < 881): indexUV = 7 
    elif (881 <= dataSensorUV) & (dataSensorUV < 976): indexUV = 8
    elif (976 <= dataSensorUV) & (dataSensorUV < 1079): indexUV = 9 
    elif (1079 <= dataSensorUV) & (dataSensorUV < 1170): indexUV = 10
    else: indexUV = 11  
    return indexUV

So, for the previous measurement (128mV), the index should be 0.

Step 9: The Complete HW & SW

At this point, we have all the sensors installed and tested. Let's develop now a function to capture all data at once:

# time library 
import datetime

# Get data (from sensors)
def getSensorData():
    global timeString
    global humLab
    global tempExt
    global tempLab
    global presSL
    global altLab
    global presAbs
    global UV_mV
    global UV_index

    now =
    timeString = now.strftime("%Y-%m-%d %H:%M")

    tempExt = round(ds18b20Sensor.get_temperature(), 1)

    tempLab, presAbs, altLab, presSL = bmp180GetData(altReal) 

    humDHT, tempDHT = Adafruit_DHT.read_retry(DHT22Sensor, DHTpin)
    if humDHT is not None and tempDHT is not None:
        humLab = round (humDHT)
    UV_mV = readSensorUV()
    UV_index = indexCalculate(UV_mV) 

Note that I have defined all sensors variables as global. You can also keep them local, returning the values from the function.

Step 10: Logging Data Locally

At this point, you have all the tools to capture a lot of data from sensors. But what to do with them?

The most simple answer is to create a single loop function to capture the data at regular bases, saving them on a local file.

with open("/home/pi/rpi_weather_station.csv", "a") as log:
    while True:
        log.write("{},{},{},{},{},{},{},{},{}\n".format(timeString, humLab, tempExt, tempLab, presSL, altLab, presAbs, UV_mV, UV_index))

The above code will open a file named "rpi_weather_station.csv" on your root directory. Every 30 seconds, the timestamp plus the data from all sensors will be "append" to this file, as you can see above.

Step 11: IoT - Sending Data to a Cloud Service

At this point, we have learned how to capture data from all sensors, saving them on a local CSV file. Now, it is time to see how to send those data to an IoT platform. On this tutorial, we will use

“ThingSpeak is an open source Internet of Things (IoT) application to store and retrieve data from things, using REST and MQTT APIs. ThingSpeak enables the creation of sensor logging applications, location tracking applications, and a social network of things with status updates."

First, you must have an account at Next, follow the instructions to create a Channel and take note of your Channel ID and Write API Key.

When creating the channel, define what info will be uploaded to each one of the 8 fields.

Step 12: MQTT Protocol and ThingSpeak Connection

MQTT is a publish/subscribe architecture that is developed primarily to connect bandwidth and power-constrained devices over wireless networks. It is a simple and lightweight protocol that runs over TCP/IP sockets or WebSockets. MQTT over WebSockets can be secured with SSL. The publish/subscribe architecture enables messages to be pushed to the client devices without the device needing to continuously poll the server.

The MQTT broker is the central point of communication, and it is in charge of dispatching all messages between the senders and the rightful receivers. A client is any device that connects to the broker and can publish or subscribe to topics to access the information. A topic contains routing information for the broker. Each client that wants to send messages publishes them to a certain topic, and each client that wants to receive messages subscribes to a certain topic. The broker delivers all messages with the matching topic to the appropriate clients.

ThingSpeak™ has an MQTT broker at the URL and port 1883. The ThingSpeak broker supports both MQTT publish and MQTT subscribe.

In our case, we will use the MQTT Publish.

For starting, let's install the Eclipse Paho MQTT Python client library, that implements versions 3.1 and 3.1.1 of the MQTT protocol.

sudo pip install paho-mqtt

Next, let's import the paho library:

import paho.mqtt.publish as publish

and initiate the Thingspeak channel and MQTT protocol. This connection method is the simplest and requires the least system resources:.


topic = "channels/" + channelID + "/publish/" + apiKey
mqttHost = ""
tTransport = "tcp"
tPort = 1883
tTLS = None<br>

Now, you must define the "payload" to be upload to your IoT service:

tPayload = "field1=" + str(humLab)+ "&field2=" + str(tempExt)+ "&field3=" + str(tempLab)+ "&field4=" + str(presSL)+ "&field5=" + str(altLab) + "&field6=" + str(presAbs)+ "&field7=" + str(UV_mV) + "&field8=" + str(UV_index)

And send it:

print ("[INFO] Data prepared to be uploaded")

    publish.single(topic, payload=tPayload, hostname=mqttHost, port=tPort, tls=tTLS, transport=tTransport)
    print ("[INFO] Data sent for 8 fields: ", humLab, tempExt, tempLab, presSL, altLab, presAbs, UV_mV, UV_index)
    print ("[INFO] Failure in sending data")

If everything is OK we will get an "Echo" of the data sent and on ThingSpeak channel page, you can see the data.

Step 13: Logging Sensor Data on IoT Service

Now, that we know that with only a few lines of code it is possible to upload data to an IoT service, let's create a loop function to do it automatically at a regular interval of time (similar to what we have done with "Logging Data Locally").

A simple code to continuously capture data, logging them on our channel would be:

import time
    tPayload = "field1=" + str(humLab)+ "&field2=" + str(tempExt)+ "&field3=" + str(tempLab)+ "&field4=" + str(presSL)+ "&field5=" + str(altLab) + "&field6=" + str(presAbs)+ "&field7=" + str(UV_mV) + "&field8=" + str(UV_index)
        publish.single(topic, payload=tPayload, hostname=mqttHost, port=tPort, tls=tTLS, transport=tTransport)
    except (KeyboardInterrupt):
        print ("[INFO] Failure in sending data") 

Looking for your ThingSpeak channel page, you will observe that the data will be loaded continuously to each field. The channel will be automatically "log" those data for future analysis. A complete CSV file of the data could be also be downloaded from the site.

We have included a function (save_Log()) to also log data locally on a CSV file:

def save_log():
    with open("/home/pi/rpi_weather_station.csv", "a") as log:
        log.write("{},{},{},{},{},{},{},{},{}\n".format(timeString, humLab, tempExt, tempLab, presSL, altLab, presAbs, UV_mV, UV_index))

The complete Jupyter notebook that was used for development can be found here: Rpi_Weather_Station.ipynb.

Step 14: ThingsView - the ThingSpeak App

The logged data can be viewed directly on local saved CSV file, on site or via an APP, for example, ThingsView!

ThingView is an APP developed by CINETICA, that enables you to visualize your ThingSpeak channels in an easy way. Just enter the channel ID and you are ready to go.

For public channels, the application will respect your windows settings: color, timescale, chart type and the number of results. The current version supports line and column charts, the spline charts are displayed as line charts.

For private channels, the data will be displayed using the default settings, as there is no way to read the private windows settings with the API key only.

The ThingView APP can be download for ANDROID and IPHONE.

Step 15: Measuring Wind Speed and Direction

This Weather Station tutorial is part of a joint project developed with my friend Mauricio Pinto. Here, we learned how to capture several important data, related to weather, as Air Temperature and Humidity, Pressure and UV. Another very important data to be added to a Weather Station are Wind Speed and Direction.

Mauricio did a great job, writing a very detailed tutorial, explained how to construct an Anemometer, mostly with recycled material. You can find his project on this 2 part tutorial:

Part 1 - Construction of the devices Anemometer and Wind Vane Direction.

Part 2 - The sketch using Arduino IDE for Esp8266 Nodemcu and transmission to ThingSpeak

As Mauricio explained in his tutorial, the anemometer is a device capable of measuring the wind speed and its direction. Using a Hall Effect sensor he was able to count how many rotations the cups give in a period of time, being the intensity of the wind is proportional to the speed of rotation of the axis. With some simple physics equations, he could determine the linear velocity of the wind, at that moment. The wind direction was measured through a windshield with a neodymium magnet and reed switches.

Here, you can see the anemometer installed in his house (around 400 meters far from my Weather Station):

The wind speed and direction were also sent to

Step 16: Conclusion

As always, I hope this project can help others find their way into the exciting world of Electronics and Data Science!

For details and final code, please visit my GitHub depository: RPi-Weather-Station

For more projects, please visit my blog:

Saludos from the south of the world!

See you in my next instructable!

Thank you,


Sensors Contest

Participated in the
Sensors Contest