Overview
In this tutorial, we will learn how to make an IoT Based Web Server Weather Station Project using Raspberry Pi Pico W & BME280 Sensor. The Raspberry Pi Pico W Web Server Web displays BME280 sensor readings using MicroPython firmware. The BME280 is a temperature, humidity, and pressure sensor that is excellent for use in mini weather stations and other similar applications.
We will create a Raspberry Pi Pico W Web Server that is mobile responsive and it can be accessed with any device with a browser in your local network. The data from the sensor is displayed on a web page in real-time with an auto-refresh feature after every 5 seconds.
Bill of Materials
We need the following components for this tutorial:
- Raspberry Pi Pico W- 1
- BME280 Sensor – 1
- Jumper Wires – 10
- Breadbaord – 1
- Micro-USB Cable – 1
BME280 Barometric Pressure Sensor
The BME280 is Barometric Pressure Sensor that can measure temperature, Humidity & Atmospheric Pressure. The Sensor has an I2C Bus and operates on 3.3V Power Supply. The unit combines high linearity and high accuracy sensors and is perfectly feasible for low current consumption, long-term stability, and high EMC robustness.
This sensor is simple to use, comes pre-calibrated, and requires no additional components, so you can start measuring relative humidity, temperature, barometric pressure, and altitude in no time.
The sensor is best for measuring humidity with ±3% accuracy, barometric pressure with ±1 hPa absolute accuracy, and temperature with ±1.0°C accuracy. Because pressure changes with altitude and the pressure measurements are so good, you can also use it as an altimeter with ±1 meter or better accuracy.
To learn more about the BME280 Sensor refer to the BME280 datasheet or you may follow the BME280 Raspberry Pi Pico interfacing guide.
Interfacing BME280 Sensor with Raspberry Pi Pico W
Let us connect the BME280 Barometric Pressure Sensor with Raspberry Pi Pico W via I2C pins. This sensor communicates using the I2C communication protocol, so the wiring is very simple.
Raspberry Pi Pico W has multiple I2C pins, you may use any. Connect the VCC, GND, SCL & SDA pin of BME280 Sensor to 3.3V, GND, GP21 & GP20 of Raspberry Pi Pico W respectively.
MicroPython Code
If you are beginner and want to learn how to get started with Raspberry Pi Pico W, you may refer to the Getting Started guide.
The code for interfacing the BME280 Sensor with Raspberry Pi Pico W is divided into two parts:
- bme280.py
- main.py
The library to read from the BME280 sensor isn’t part of the standard MicroPython library by default. So, you need to upload the BME280 MicroPython library to your Raspberry Pi Pico W Board. The library has all the required modules and registers details to extract the data from the Sensor.
bme280.py
Open your Thonny IDE and paste the following code to the Thonny Editor. Save the file in the Raspberry Pi Pico W and name it bme280.py.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
import time from ustruct import unpack, unpack_from from array import array # BME280 default address. BME280_I2CADDR = 0x76 # Operating Modes BME280_OSAMPLE_1 = 1 BME280_OSAMPLE_2 = 2 BME280_OSAMPLE_4 = 3 BME280_OSAMPLE_8 = 4 BME280_OSAMPLE_16 = 5 BME280_REGISTER_CONTROL_HUM = 0xF2 BME280_REGISTER_CONTROL = 0xF4 class BME280: def __init__(self, mode=BME280_OSAMPLE_1, address=BME280_I2CADDR, i2c=None, **kwargs): # Check that mode is valid. if mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4, BME280_OSAMPLE_8, BME280_OSAMPLE_16]: raise ValueError( 'Unexpected mode value {0}. Set mode to one of ' 'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or ' 'BME280_ULTRAHIGHRES'.format(mode)) self._mode = mode self.address = address if i2c is None: raise ValueError('An I2C object is required.') self.i2c = i2c # load calibration data dig_88_a1 = self.i2c.readfrom_mem(self.address, 0x88, 26) dig_e1_e7 = self.i2c.readfrom_mem(self.address, 0xE1, 7) self.dig_T1, self.dig_T2, self.dig_T3, self.dig_P1, \ self.dig_P2, self.dig_P3, self.dig_P4, self.dig_P5, \ self.dig_P6, self.dig_P7, self.dig_P8, self.dig_P9, \ _, self.dig_H1 = unpack("<HhhHhhhhhhhhBB", dig_88_a1) self.dig_H2, self.dig_H3 = unpack("<hB", dig_e1_e7) e4_sign = unpack_from("<b", dig_e1_e7, 3)[0] self.dig_H4 = (e4_sign << 4) | (dig_e1_e7[4] & 0xF) e6_sign = unpack_from("<b", dig_e1_e7, 5)[0] self.dig_H5 = (e6_sign << 4) | (dig_e1_e7[4] >> 4) self.dig_H6 = unpack_from("<b", dig_e1_e7, 6)[0] self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL, bytearray([0x3F])) self.t_fine = 0 # temporary data holders which stay allocated self._l1_barray = bytearray(1) self._l8_barray = bytearray(8) self._l3_resultarray = array("i", [0, 0, 0]) def read_raw_data(self, result): """ Reads the raw (uncompensated) data from the sensor. Args: result: array of length 3 or alike where the result will be stored, in temperature, pressure, humidity order Returns: None """ self._l1_barray[0] = self._mode self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL_HUM, self._l1_barray) self._l1_barray[0] = self._mode << 5 | self._mode << 2 | 1 self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL, self._l1_barray) sleep_time = 1250 + 2300 * (1 << self._mode) sleep_time = sleep_time + 2300 * (1 << self._mode) + 575 sleep_time = sleep_time + 2300 * (1 << self._mode) + 575 time.sleep_us(sleep_time) # Wait the required time # burst readout from 0xF7 to 0xFE, recommended by datasheet self.i2c.readfrom_mem_into(self.address, 0xF7, self._l8_barray) readout = self._l8_barray # pressure(0xF7): ((msb << 16) | (lsb << 8) | xlsb) >> 4 raw_press = ((readout[0] << 16) | (readout[1] << 8) | readout[2]) >> 4 # temperature(0xFA): ((msb << 16) | (lsb << 8) | xlsb) >> 4 raw_temp = ((readout[3] << 16) | (readout[4] << 8) | readout[5]) >> 4 # humidity(0xFD): (msb << 8) | lsb raw_hum = (readout[6] << 8) | readout[7] result[0] = raw_temp result[1] = raw_press result[2] = raw_hum def read_compensated_data(self, result=None): """ Reads the data from the sensor and returns the compensated data. Args: result: array of length 3 or alike where the result will be stored, in temperature, pressure, humidity order. You may use this to read out the sensor without allocating heap memory Returns: array with temperature, pressure, humidity. Will be the one from the result parameter if not None """ self.read_raw_data(self._l3_resultarray) raw_temp, raw_press, raw_hum = self._l3_resultarray # temperature var1 = ((raw_temp >> 3) - (self.dig_T1 << 1)) * (self.dig_T2 >> 11) var2 = (((((raw_temp >> 4) - self.dig_T1) * ((raw_temp >> 4) - self.dig_T1)) >> 12) * self.dig_T3) >> 14 self.t_fine = var1 + var2 temp = (self.t_fine * 5 + 128) >> 8 # pressure var1 = self.t_fine - 128000 var2 = var1 * var1 * self.dig_P6 var2 = var2 + ((var1 * self.dig_P5) << 17) var2 = var2 + (self.dig_P4 << 35) var1 = (((var1 * var1 * self.dig_P3) >> 8) + ((var1 * self.dig_P2) << 12)) var1 = (((1 << 47) + var1) * self.dig_P1) >> 33 if var1 == 0: pressure = 0 else: p = 1048576 - raw_press p = (((p << 31) - var2) * 3125) // var1 var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25 var2 = (self.dig_P8 * p) >> 19 pressure = ((p + var1 + var2) >> 8) + (self.dig_P7 << 4) # humidity h = self.t_fine - 76800 h = (((((raw_hum << 14) - (self.dig_H4 << 20) - (self.dig_H5 * h)) + 16384) >> 15) * (((((((h * self.dig_H6) >> 10) * (((h * self.dig_H3) >> 11) + 32768)) >> 10) + 2097152) * self.dig_H2 + 8192) >> 14)) h = h - (((((h >> 15) * (h >> 15)) >> 7) * self.dig_H1) >> 4) h = 0 if h < 0 else h h = 419430400 if h > 419430400 else h humidity = h >> 12 if result: result[0] = temp result[1] = pressure result[2] = humidity return result return array("i", (temp, pressure, humidity)) @property def values(self): """ human readable values """ t, p, h = self.read_compensated_data() p = p // 256 pi = p // 100 pd = p - pi * 100 hi = h // 1024 hd = h * 100 // 1024 - hi * 100 return ("{}*C".format(t / 100), "{}.{:02d} hPa".format(pi, pd), "{}.{:02d} %".format(hi, hd)) |
main.py
The code for creating a Raspberry Pi Pico W-based Web Server Weather Station is simple. In the following code change the WiFi SSID and password.
|
1 |
wlan.connect("**********","**********") |
Save the file to Raspberry Pi Pico W with the name main.py or anything else.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
from machine import Pin, I2C #importing relevant modules & classes from time import sleep import utime import socket import network import bme280 #importing BME280 library i2c=I2C(0,sda=Pin(20), scl=Pin(21), freq=400000) #initializing the I2C method wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect("**********","**********") def web_page(): bme = bme280.BME280(i2c=i2c) #BME280 object created html = """<html><head><meta http-equiv="refresh" content="5"><meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" href="data:,"><style>body { text-align: center; font-family: "Helvetica", Arial;} table { border-collapse: collapse; width:55%; margin-left:auto; margin-right:auto; } th { padding: 12px; background-color: #87034F; color: white; } tr { border: 2px solid #000556; padding: 12px; } tr:hover { background-color: #bcbcbc; } td { border: none; padding: 14px; } .sensor { color:DarkBlue; font-weight: bold; background-color: #ffffff; padding: 1px; </style></head><body><h1>BME280 Pi Pico W Weather Station</h1> <table><tr><th>Parameters</th><th>Value</th></tr> <tr><td>Temperature</td><td><span class="sensor">""" + str(bme.values[0]) + """</span></td></tr> <tr><td>Pressure</td><td><span class="sensor">""" + str(bme.values[1]) + """</span></td></tr> <tr><td>Humidity</td><td><span class="sensor">""" + str(bme.values[2]) + """</span></td></tr> <head><meta http-equiv="refresh" content="5"><meta name="viewport" content="width=device-width, initial-scale=1"><style>img{display: block; margin-left: auto; margin-right: auto;}</style></head><body><img src="data:image/jpeg;base64,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" alt="pico" style="max-width:100%;"> </body></html>""" return html # Wait for connect or fail wait = 10 while wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break wait -= 1 print('waiting for connection...') time.sleep(1) # Handle connection error if wlan.status() != 3: raise RuntimeError('wifi connection failed') else: print('connected') ip=wlan.ifconfig()[0] print('IP: ', ip) # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('', 80)) server.listen(5) print('listening on', addr) while True: try: conn, addr = server.accept() conn.settimeout(3.0) print('client connected from', addr) request = conn.recv(1024) conn.settimeout(None) # HTTP-Request receive print('Request:', request) # HTTP-Response send response = web_page() conn.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') conn.sendall(response) conn.close() except OSError as e: conn.close() print('connection closed') |
Testing IoT Based BME280 Web Server Weather Station
In this project, we’ll display sensor readings from the BME280 temperature, humidity and pressure sensor on a web server that you can access on your local network.
Run the above code. The Raspberry Pi Pico W will connect to the WiFi Network using the given network credentials. Then the Python Shell will display the IP Address of the Raspberry Pi Pico W.
Copy the IP Address and paste it on your Web Browser and hit enter. You should get a page with the latest sensor readings as shown in the following figure.
With the web server script provided in this project, you don’t need to refresh the web page to see the latest readings as the web page refreshes automatically after an interval of every 5 seconds.
On the Python Shell, you will able to see the following messages and response together.
You can check the Sensor reading even from multiple devices at the same time.
You can use a similar code to display the sensor reading on a Web Server.













9 Comments
When I try to open webpage from IP, it says ” This page isn’t working” ; “192.168.100.15 didn’t send any data” and “ERR_EMPTY_RESPONSE”.
Thank you. That looks interesting. I have a similar problem to Vlad. The page does not display in the browser. The messages in the Thonny confirm that the client has connected. If I remove the first line in def web_page (bme = bme.280…) and related instructions in the table, then the page displays, so is there a problem with bme280.py, or is it with my sensor?
Having an issue where it won’t start on boot, yet will work if I run the script from Thonny. Any ideas?
Vlad/ineshed – Same results here. Connects OK but browser message is “This page isn’t working 192.168.1.66 didn’t send any data.
ERR_EMPTY_RESPONSE”
Has this been resolved yet? How can I tell if any data was sent?
I have reflashed the Pico W several times. This error will come up after the first run. I copied the code, inserted my wireless information. This error in line 41 is something I cannot get past trying other editors. Any help would be appreciated.
MPY: soft reboot
waiting for connection…
Traceback (most recent call last):
File “”, line 6, in
File “bme280.py”, line 41, in
NameError: name ‘time’ isn’t defined
I have reflashed the Pico W several times. This error will come up after the first run. I copied the code, inserted my wireless information. This error in line 41 is something I cannot get past trying other editors. Any help would be appreciated.
MPY: soft reboot
waiting for connection…
Traceback (most recent call last):
File “”, line 6, in
File “bme280.py”, line 41, in
NameError: name ‘time’ isn’t defined
I at least have it running without errors. However I am seeing this: This connected address is a router address. And the listening address? cannot log into to see the web page.
MPY: soft reboot
connected
IP: 192.168.1.227
listening on (‘0.0.0.0’, 80)
Line 41 error – it should read uptime rather than time to activate the sleep command.
Worked for me!
i had the same Problem with main.py “not starting” when not connected to thonny. There seems to be a Problem in “# Wait for connect or fail” and “# Handle connection error” i changed uptime in line 42 to utime and ip=wlan.ifconfig()[0] in line 50 to ip = wlan.ifconfig()[0]. now its working without thonny