---
# System prepended metadata

title: Weather station with two sensors

---

# Weather station with two sensors

Author: Peter Hultgren (ph222wn)

The project aims to collect weather data and transfer it from a development board with a microcontroller to a platform in the cloud and present a selection of this data visually.

A Lopy4 processor connected to two sensors (DHT11 & BME280) is used to collect temperature, humidity and pressure. This data is then sent to Ubidots cloud service.

By following this tutorial you should be able to do the project in a day or two depending on prior knowledge.


## Objective

I chose this project because I had access to sensors to collect weather data and I could not come up with a really interesting idea within a reasonable time.

I wanted to gain more knowledge about different ways to send data from a microprocessor to different cloud services. In this case, the choice of data did not play such a big role and sending weather data became a good choice.

The purpose of the project is to collect data from a DHT11 (Temprature and Humidity), a BME280 (Temprature, Humidity and Pressure) and present selected data on Ubisoft's cloud service.

I hope that insights from the project will give me better knowledge of different ways to transfer data with different protocols and present data with the help of cloud services.
I also want to get feedback from putting the processor in deep sleep to save battery.

## Material

| Hardware                 | Links           |Cost (notes) |
| -------------------------|-----------------|------|
| Lopy4                    | [:link:][Lopy4] |€34.95 (1)
| Expansion Board 3.0      | [:link:][Board3]|€16.00 (1)
| DHT11 Sensor             | [:link:][DHT11] |SEK 49 (2)
| BME280 Sensor            | None            |SEK 39 (3)
| Li-Po battery 3.7 V      | [:link:][Li-Po] |SEK 99 (4)
| Breadboard               | [:link:][Bread] |SEK 59 (1)
| dupont cables (male/male)| [:link:][Dupont]|SEK 29 (1)
| USB cable to Micro B     | None            |SEK 99 (1)
| toggle switch            | None            |SEK 1 (3)

[Lopy4]: https://pycom.io/product/lopy4/
[Board3]: https://pycom.io/product/expansion-board-3-0/
[DHT11]: https://www.electrokit.com/produkt/digital-temperatur-och-fuktsensor-dht11/
[Li-Po]: https://www.kjell.com/se/produkter/el-verktyg/arduino/tillbehor/luxorparts-li-po-batteri-37-v-med-kontakt-1200-mah-p87924
[Bread]: https://www.electrokit.com/produkt/kopplingsdack-400-anslutningar/
[Dupont]: https://www.electrokit.com/produkt/labbsladd-20-pin-15cm-hane-hane/

Notes:

(1) : I bought Lopy4, Expansion Board 3.0, Bradboard, Jumper wires (dupont cables) and Micro USB cable together with som other stuff in a bundle from elekrokit for SEK 995. You can also use other suitable jumper wires. You may already have a suitable USB cable. Be aware that some cheap cables can only be used for charging and not for data transfer.

(2) : The DHT11 sensor was included in a sensor kit (25 modules) from elekrokit which cost SEK 299.

(3) : I bought a BMP280 sensor and a switch for about 40 SEK a while ago from a family member who bought them from Aliexpress.

(4) : I bought the LiPo battery from Kjell & Company.

The LoPy4 is Micropython-programmable board. It works with LoRa, Sigfox, WiFi and Bluetooth.

An Expansion Board 3.0 makes it easier to connect to the computer via USB cable. The expansion board can also charge a LiPo battery. When the card is not connected via micro-USB, it can be powered via JST connector with LiPo battery or other power source.

DHT11 is a simple temprature and humidity sensor that have a 1°C Resolution.

BME280 is temprature, humidity and pressure sensor that have a 0.01°C Resolution.

Jumper wires and breadboard are used to connect sensors and toggle switch with expansion card 3.0. Lopy4 is mounted in the expansion card. The toggle switch is used to make it easier to interrupt program execution in deep sleep.

In-depth information about the hardware can be obtained via the links in the table above.

## Computer setup

I have chosen to use Atom as an editor on a computer running Windows 10.

Follow this steps to download software and update firmware. Click on the links to get it right.

Install Atom and Pymakr plugin:

- Download [Atom](https://atom.io/).
- Download [Node.JS](https://nodejs.org/en/) for Pymakr.
- Install [Pymakr](https://docs.pycom.io/pymakr/installation/atom/) plugin.

Connect to Expansion Board 3.0 from computer:

- [Install](https://docs.pycom.io/gettingstarted/connection/lopy4/) the Lopy4 module on the expansion board.
- Connect the USB cable between a USB socket on the computer and the expansion card's micro usb socket.
- [Connect](https://docs.pycom.io/pymakr/installation/atom/) to the Expansion Card from Atom

Update Lopy4 firmware:

- Uppdating [firmware](https://docs.pycom.io/gettingstarted/installation/firmwaretool/) for Lopy4

Uploaing the Code:

- Best described in Pycom [getting started guide](https://docs.pycom.io/gettingstarted/programming/)

I urge you to go to Pycom's [documentation](https://docs.pycom.io/) if any other problems arise. Some people have needed to update Expansion Board 3.0, but I had no problem skipping this step.

## Putting everything together

Connect the following pins ond the expansion board to the connections on the sensors:

- 3.3V to VDD on DHT11 and VIN in BME280
- GND to GND on DHT11 and GND on BME280
- P23 to DATA on DHT11
- P9 to SDA on BME280
- P10 to SCL on BME280
- P22 to output pin from the switch. 
- GND and 3.3V to the respective input pins on the switch.
- LiPo battery for JST connector on expansion card.

Circuit diagram:

![](https://i.imgur.com/6MNg7WJ.jpg)

You can replace a lipo battery with another corresponding power source or provide power via the USB connector. The switch is not needed but should be seen as a convenient way to interrupt the program after deep sleep.

This is just a development setting and should not be used in production. I would find a cheaper processor and consider rewriting the program in C / C ++. You also do not need a breaboard in a production. Normally, breadboard is not used in production either.


## Platform

I chose to use Ubidots as it was easy to find a way to connect to a pycom device. Ubidots is a cloud solution and it is free to connect up to three devices for private use. There are many different possibilities to present data, which made it more interesting than Pycom's own platform, which I tested first. I like that it is quick to get a dashboard in Ubidots and that it is possible to connect in different ways.


## The code

The code consists of the required files boot and main with extension .py.
The lib directory consists of the following files: 

| lib directory      | Note           |
| -------------------|-----------------|
| [BME280.py][1]    | External library|
| [dht.py][2]       | External library (renamed)|
| ubidots_config.py | Ubidots Token (2)|
|[ubidots.py][3]    | Revised code from link (1)          |
| [urequsts.py][4]  | External library |
| wify_config.py    | Wifi credentials (2) |
| [wify.py][3]      | revised code link (1)|

[1]: https://github.com/RuiSantosdotme/ESP-MicroPython/blob/master/code/WiFi/HTTP_Client_IFTTT_BME280/BME280.py
[2]: https://github.com/JurassicPork/DHT_PyCom/blob/master/dth.py
[3]: https://help.ubidots.com/en/articles/961994-connect-any-pycom-board-to-ubidots-using-wi-fi-over-http
[4]: https://github.com/jotathebest/micropython-lib/blob/master/urequests/urequests.py

(1) Step three of the following [turtorial](https://help.ubidots.com/en/articles/961994-connect-any-pycom-board-to-ubidots-using-wi-fi-over-http) has been used as the basis for these two libraries. The code has since been rewritten to make it more general. 
(2) Here you set your own values for token (ubidots) and Wifi (network and password) respectively. The files are therefore not listed.


Here are the python files that are not included in the links in the table above (main, ubidots and wify python files)

`main.py:`

```python
# Collect data from DHT11 and BME280 sensors
# Send data to ubidots
# --------------------

# Micropyton and pycom lib
import time
from machine import Pin, I2C
import pycom

#Local lib (/lib)
import wifi
import ubidots

# Sensor libraries (/lib) from external sources
from dht import DHT # https://github.com/JurassicPork/DHT_PyCom
import BME280 # https://github.com/RuiSantosdotme/ESP-MicroPython/blob/master/code/WiFi/HTTP_Client_IFTTT_BME280/BME280.py

# Delay values
sleep_delay = 15 * 60 # Delay in seconds
deep_sleep_delay = sleep_delay * 1000 # deep sleep in ms

# Main Program
def main():

    # Connect to Wifi and check status
    wifi_status(wifi.connect()) # Setup and check Wifi connection

    # Variabelnames sent to ubidots
    variable_names = ["Temperature1_C", "Humidity1_RH", "Temperature2_C", "Humidity2_RH", "Pressure_hPa"]

    while True:
        # Get data from sensors
        i2c = init_BME280()
        temp_DHT, hum_DHT = get_DHT_data()
        temp_BME280, hum_BME280, pres_BME280 = get_BME280_data(i2c)

        # Variabel values sent to ubidots
        variable_values = [temp_DHT, hum_DHT, temp_BME280, hum_BME280, pres_BME280]
        #display_data(variable_values) # Only for test
        publish_data(variable_names, variable_values) # Data to ubidots
        sleep_BME280(i2c)

        # Quit program if switch in position on (true=Vin)
        if quit_program():
            print("Quit program\n")
            break
        else: # Deep sleep (Normal mode)
            print("deep sleep selected\n")
            machine.deepsleep(deep_sleep_delay)
            #time.sleep(sleep_delay) # Only for test
    print("Program ended")

# Functions
# ---------

# Check if connected to Wifi
def wifi_status(isconnected):
    pycom.heartbeat(False) # heartbeat off
    if isconnected:
        #pycom.rgbled(0x00FF00) # Green
        print("Connected to Wifi\n")
    else:
        pycom.rgbled(0xFF0000) # Red
        print("Network error: can't connect\n")

# Publish data
def publish_data(variable_names, variable_values):
    ubidots.post_var("pycom", variable_names, variable_values)

# Quit the program
def quit_program():
    quit_program_pin = Pin('P22', mode=Pin.IN, pull=Pin.PULL_DOWN)
    return bool(quit_program_pin())

# Display data in REPL
def display_data(variable_values):
    variable_texts = ['DHT Temperature (C):',
                      'DHT Humidity (RH):',
                      'BME280: Temperature (C): ',
                      'BME280: Humidity (RH): ',
                      'BME280: Pressure (hPa): ']
    for i in range(len(variable_texts)):
        print(variable_texts[i], variable_values[i])

# DHT11 sensor
def get_DHT_data():
    # Type 0 = dht11
    # Type 1 = dht22
    th = DHT(Pin('P23', mode=Pin.OPEN_DRAIN), 0)
    time.sleep(2)
    result = th.read()
    while not result.is_valid():
        time.sleep(.5)
        result = th.read()
    return result.temperature, result.humidity

# BME280 sensor

# Initialize I2C for BME280
def init_BME280():
    i2c = I2C(0)                         # create on bus 0
    i2c = I2C(0, I2C.MASTER)             # create and init as a master
    i2c = I2C(0, pins=('P9','P10'))      # PIN assignments (P9=SDA, P10=SCL)
    i2c.init(I2C.MASTER, baudrate=10000) # init as a master
    return i2c

# Read data from BME280
def get_BME280_data(i2c):
    bme = BME280.BME280(i2c=i2c) # Get the data from BME280
    temp_BME280 = float(bme.temperature[:-1]) # Remove the tast character: C
    hum_BME280 = float(bme.humidity[:-1]) # Remove the tast character: %
    pres_BME280 = float(bme.pressure[:-3]) # Remove the last three characters: hPa
    return temp_BME280, hum_BME280, pres_BME280

# Deinit function
def sleep_BME280(i2c):
    i2c.deinit()

# Call main function
if __name__ == "__main__":
    main()

```

`lib/ubidots.py:`

```python
# Send data to ubidots
# --------------------
# Modified and more general code from step 3 in the following article
# https://help.ubidots.com/en/articles/961994-connect-any-pycom-board-to-ubidots-using-wi-fi-over-http

# Source: https://github.com/jotathebest/micropython-lib/blob/master/urequests/urequests.py
import urequests as requests

# Configuration file for token (user specifik file)
import ubidots_config

TOKEN = ubidots_config.token #Give your TOKEN here

# Builds the json to send the request
def build_json(variable_names, variable_values):
    try:
        data = {} # Empty dictionary
        for i in range(len(variable_values)):
            data.update({variable_names[i]: {'value': variable_values[i]}})
        return data # dict (dictionary) is a json representation
    except:
        return None

# Sends the request. Please reference the REST API reference https://ubidots.com/docs/api/
def post_var(device, variable_names, variable_values):
    try:
        url = "https://industrial.api.ubidots.com/"
        url = url + "api/v1.6/devices/" + device
        headers = {"X-Auth-Token": TOKEN, "Content-Type": "application/json"}
        data = build_json(variable_names, variable_values)
        if data is not None:
            #print(data) # Only for test
            req = requests.post(url=url, headers=headers, json=data)
            return req.json()
        else:
            pass
    except:
        pass

```

`lib/wify.py`

```python
# Set up and connect to wifi
# --------------------------
# Modified code from step 3 in the following article
# https://help.ubidots.com/en/articles/961994-connect-any-pycom-board-to-ubidots-using-wi-fi-over-http

from network import WLAN
import machine
import utime
import time
import wifi_config

# Connect to Wifi
def connect():

    # Assign your Wi-Fi credentials
    SSID = wifi_config.ssid
    PASSWORD = wifi_config.password

    # Max wait for Wifi connection
    max_wait_for_wlan = 60000 # 60 s

    wlan = WLAN(mode=WLAN.STA)
    wlan.antenna(WLAN.INT_ANT)

    time_before_connect = utime.ticks_ms()
    wlan.connect(SSID, auth=(WLAN.WPA2, PASSWORD), timeout=5000)

    while not wlan.isconnected():
        machine.idle()
        if utime.ticks_diff(utime.ticks_ms(), time_before_connect) > max_wait_for_wlan:
            break # Break out of loop if Wifi not connected in time
    return wlan.isconnected()

```

## Transmitting the data / connectivity

Data is sent every fifteen minutes via Wi-Fi using a Rest Api to Ubidots. The processor enters Deep Sleep mode to save power for 15 minutes and then takes less than 20 seconds to send data if Wifi is connected.

I used Wi-Fi because I can not connect to the LoRA where I live. First, some attempts were made to connect to the Lora network, but I can't connect where I live. This is because I'm out of range or the nearest gateway is not working. Lora would provide less power consumption while making it more important to send more compact data.

Data is sent to Ubidots in JSON format and I have made the code general so that I can more easily connect more sensors to the main program in the future. 

## Presenting the data

Link to my weather data:[:link:][Ubidots]

[Ubidots]: https://industrial.ubidots.com/app/dashboards/public/dashboard/yrtyDDtFmNJ6iHqzA-LsjwllQns

Example of what my dashboard looks like from my Windows computer:

![](https://i.imgur.com/SqNYEeL.png)

Example of what my dashboard looks like from my Ipad:

![](https://i.imgur.com/pJdti53.png)

Data is saved on Ubidots every fifteen minutes when the system is running. I chose Ubidots because it did not require me to have a local server or use Linnaeus University's resources.

I have chosen to show a graph of data from the two sensors during the current day. I also present the current value of temperature, humidity and pressure from the BME280 sensor as it is more accurate.

The presentation store is made in Ubidots using widgets in a dashboard. The tool is intuitive to use so just get an account and test.

## Finalizing the design

The end result was that I got an insight into collecting data and presenting it on a cloud service. I have also gained insight into what different protocols there are for transferring data to the cloud. I have tried using micropython in a microprocessor which was new to me. I think Python is a good choice at a basic level, although I think it's easier to have control over the code in C / C ++.

If I had had more time, I would have tested several sensors and reduced the battery consumption further by adding a capacitor to switch the power to the sensors.

![](https://i.imgur.com/dUBpxBj.jpg)

![](https://i.imgur.com/NMTImuJ.jpg)
