Tutorial ini adalah panduan langkah demi langkah untuk membangun Web Server mandiri pada ESP32 atau ESP8266 NodeMCU yang menampilkan pembacaan sensor BME280 menggunakan firmware MicroPython. Kami akan membuat Web Server di ESP32/ESP8266 yang responsif di perangkat seluler dan dapat diakses oleh perangkat apa pun yang memiliki browser dalam jaringan lokal Anda.
Mengenal Modul Sensor BME280
Modul sensor BME280 membaca tekanan barometrik, suhu, dan kelembaban. Karena tekanan berubah seiring ketinggian, Anda juga dapat memperkirakan ketinggian (altitude). Ada beberapa versi modul sensor ini, tetapi kami menggunakan versi yang ditunjukkan pada gambar di bawah.
Sensor ini berkomunikasi menggunakan protokol I2C, sehingga koneksi kabelnya sangat sederhana. Anda dapat menggunakan pin I2C default pada ESP32 atau ESP8266 seperti yang ditunjukkan pada tabel berikut:
Komponen yang Diperlukan
Untuk proyek ini, Anda perlu menghubungkan modul sensor BME280 ke pin I2C ESP32 atau ESP8266. Berikut adalah daftar komponen yang diperlukan untuk tutorial ini:
- Modul sensor BME280
- ESP32 atau ESP8266 (baca: [Perbandingan ESP32 vs ESP8266](tautan))
- Papan breadboard
- Kabel jumper
Skematik – ESP32
Ikuti diagram skematik berikut jika Anda menggunakan papan ESP32:
Skematik – ESP8266
Ikuti diagram skematik berikut jika Anda menggunakan papan ESP8266:
Library MicroPython untuk BME280
Library untuk membaca data dari sensor BME280 tidak termasuk dalam pustaka standar MicroPython secara default. Oleh karena itu, Anda perlu mengunggah pustaka berikut ke papan ESP32/ESP8266 Anda (simpan dengan nama `bme280.py`).
from machine import I2C
import time
# 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 Registers
BME280_REGISTER_DIG_T1 = 0x88 # Trimming parameter registers
BME280_REGISTER_DIG_T2 = 0x8A
BME280_REGISTER_DIG_T3 = 0x8C
BME280_REGISTER_DIG_P1 = 0x8E
BME280_REGISTER_DIG_P2 = 0x90
BME280_REGISTER_DIG_P3 = 0x92
BME280_REGISTER_DIG_P4 = 0x94
BME280_REGISTER_DIG_P5 = 0x96
BME280_REGISTER_DIG_P6 = 0x98
BME280_REGISTER_DIG_P7 = 0x9A
BME280_REGISTER_DIG_P8 = 0x9C
BME280_REGISTER_DIG_P9 = 0x9E
BME280_REGISTER_DIG_H1 = 0xA1
BME280_REGISTER_DIG_H2 = 0xE1
BME280_REGISTER_DIG_H3 = 0xE3
BME280_REGISTER_DIG_H4 = 0xE4
BME280_REGISTER_DIG_H5 = 0xE5
BME280_REGISTER_DIG_H6 = 0xE6
BME280_REGISTER_DIG_H7 = 0xE7
BME280_REGISTER_CHIPID = 0xD0
BME280_REGISTER_VERSION = 0xD1
BME280_REGISTER_SOFTRESET = 0xE0
BME280_REGISTER_CONTROL_HUM = 0xF2
BME280_REGISTER_CONTROL = 0xF4
BME280_REGISTER_CONFIG = 0xF5
BME280_REGISTER_PRESSURE_DATA = 0xF7
BME280_REGISTER_TEMP_DATA = 0xFA
BME280_REGISTER_HUMIDITY_DATA = 0xFD
class Device:
"""Class for communicating with an I2C device.
Allows reading and writing 8-bit, 16-bit, and byte array values to
registers on the device."""
def __init__(self, address, i2c):
"""Create an instance of the I2C device at the specified address using
the specified I2C interface object."""
self._address = address
self._i2c = i2c
def writeRaw8(self, value):
"""Write an 8-bit value on the bus (without register)."""
value = value & 0xFF
self._i2c.writeto(self._address, value)
def write8(self, register, value):
"""Write an 8-bit value to the specified register."""
b=bytearray(1)
b[0]=value & 0xFF
self._i2c.writeto_mem(self._address, register, b)
def write16(self, register, value):
"""Write a 16-bit value to the specified register."""
value = value & 0xFFFF
b=bytearray(2)
b[0]= value & 0xFF
b[1]= (value>>8) & 0xFF
self.i2c.writeto_mem(self._address, register, value)
def readRaw8(self):
"""Read an 8-bit value on the bus (without register)."""
return int.from_bytes(self._i2c.readfrom(self._address, 1),'little') & 0xFF
def readU8(self, register):
"""Read an unsigned byte from the specified register."""
return int.from_bytes(
self._i2c.readfrom_mem(self._address, register, 1),'little') & 0xFF
def readS8(self, register):
"""Read a signed byte from the specified register."""
result = self.readU8(register)
if result > 127:
result -= 256
return result
def readU16(self, register, little_endian=True):
"""Read an unsigned 16-bit value from the specified register, with the
specified endianness (default little endian, or least significant byte
first)."""
result = int.from_bytes(
self._i2c.readfrom_mem(self._address, register, 2),'little') & 0xFFFF
if not little_endian:
result = ((result << 8) & 0xFF00) + (result >> 8)
return result
def readS16(self, register, little_endian=True):
"""Read a signed 16-bit value from the specified register, with the
specified endianness (default little endian, or least significant byte
first)."""
result = self.readU16(register, little_endian)
if result > 32767:
result -= 65536
return result
def readU16LE(self, register):
"""Read an unsigned 16-bit value from the specified register, in little
endian byte order."""
return self.readU16(register, little_endian=True)
def readU16BE(self, register):
"""Read an unsigned 16-bit value from the specified register, in big
endian byte order."""
return self.readU16(register, little_endian=False)
def readS16LE(self, register):
"""Read a signed 16-bit value from the specified register, in little
endian byte order."""
return self.readS16(register, little_endian=True)
def readS16BE(self, register):
"""Read a signed 16-bit value from the specified register, in big
endian byte order."""
return self.readS16(register, little_endian=False)
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
# Create I2C device.
if i2c is None:
raise ValueError('An I2C object is required.')
self._device = Device(address, i2c)
# Load calibration values.
self._load_calibration()
self._device.write8(BME280_REGISTER_CONTROL, 0x3F)
self.t_fine = 0
def _load_calibration(self):
self.dig_T1 = self._device.readU16LE(BME280_REGISTER_DIG_T1)
self.dig_T2 = self._device.readS16LE(BME280_REGISTER_DIG_T2)
self.dig_T3 = self._device.readS16LE(BME280_REGISTER_DIG_T3)
self.dig_P1 = self._device.readU16LE(BME280_REGISTER_DIG_P1)
self.dig_P2 = self._device.readS16LE(BME280_REGISTER_DIG_P2)
self.dig_P3 = self._device.readS16LE(BME280_REGISTER_DIG_P3)
self.dig_P4 = self._device.readS16LE(BME280_REGISTER_DIG_P4)
self.dig_P5 = self._device.readS16LE(BME280_REGISTER_DIG_P5)
self.dig_P6 = self._device.readS16LE(BME280_REGISTER_DIG_P6)
self.dig_P7 = self._device.readS16LE(BME280_REGISTER_DIG_P7)
self.dig_P8 = self._device.readS16LE(BME280_REGISTER_DIG_P8)
self.dig_P9 = self._device.readS16LE(BME280_REGISTER_DIG_P9)
self.dig_H1 = self._device.readU8(BME280_REGISTER_DIG_H1)
self.dig_H2 = self._device.readS16LE(BME280_REGISTER_DIG_H2)
self.dig_H3 = self._device.readU8(BME280_REGISTER_DIG_H3)
self.dig_H6 = self._device.readS8(BME280_REGISTER_DIG_H7)
h4 = self._device.readS8(BME280_REGISTER_DIG_H4)
h4 = (h4 << 24) >> 20
self.dig_H4 = h4 | (self._device.readU8(BME280_REGISTER_DIG_H5) & 0x0F)
h5 = self._device.readS8(BME280_REGISTER_DIG_H6)
h5 = (h5 << 24) >> 20
self.dig_H5 = h5 | (
self._device.readU8(BME280_REGISTER_DIG_H5) >> 4 & 0x0F)
def read_raw_temp(self):
"""Reads the raw (uncompensated) temperature from the sensor."""
meas = self._mode
self._device.write8(BME280_REGISTER_CONTROL_HUM, meas)
meas = self._mode << 5 | self._mode << 2 | 1
self._device.write8(BME280_REGISTER_CONTROL, meas)
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
msb = self._device.readU8(BME280_REGISTER_TEMP_DATA)
lsb = self._device.readU8(BME280_REGISTER_TEMP_DATA + 1)
xlsb = self._device.readU8(BME280_REGISTER_TEMP_DATA + 2)
raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4
return raw
def read_raw_pressure(self):
"""Reads the raw (uncompensated) pressure level from the sensor."""
"""Assumes that the temperature has already been read """
"""i.e. that enough delay has been provided"""
msb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA)
lsb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA + 1)
xlsb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA + 2)
raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4
return raw
def read_raw_humidity(self):
"""Assumes that the temperature has already been read """
"""i.e. that enough delay has been provided"""
msb = self._device.readU8(BME280_REGISTER_HUMIDITY_DATA)
lsb = self._device.readU8(BME280_REGISTER_HUMIDITY_DATA + 1)
raw = (msb << 8) | lsb
return raw
def read_temperature(self):
"""Get the compensated temperature in 0.01 of a degree celsius."""
adc = self.read_raw_temp()
var1 = ((adc >> 3) - (self.dig_T1 << 1)) * (self.dig_T2 >> 11)
var2 = ((
(((adc >> 4) - self.dig_T1) * ((adc >> 4) - self.dig_T1)) >> 12) *
self.dig_T3) >> 14
self.t_fine = var1 + var2
return (self.t_fine * 5 + 128) >> 8
def read_pressure(self):
"""Gets the compensated pressure in Pascals."""
adc = self.read_raw_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:
return 0
p = 1048576 - adc
p = (((p << 31) - var2) * 3125) // var1
var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25
var2 = (self.dig_P8 * p) >> 19
return ((p + var1 + var2) >> 8) + (self.dig_P7 << 4)
def read_humidity(self):
adc = self.read_raw_humidity()
# print 'Raw humidity = {0:d}'.format (adc)
h = self.t_fine - 76800
h = (((((adc << 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
return h >> 12
@property
def temperature(self):
"Return the temperature in degrees."
t = self.read_temperature()
ti = t // 100
td = t - ti * 100
return "{}.{:02d}C".format(ti, td)
@property
def pressure(self):
"Return the temperature in hPa."
p = self.read_pressure() // 256
pi = p // 100
pd = p - pi * 100
return "{}.{:02d}hPa".format(pi, pd)
@property
def humidity(self):
"Return the humidity in percent."
h = self.read_humidity()
hi = h // 1024
hd = h * 100 // 1024 - hi * 100
return "{}.{:02d}%".format(hi, hd)
Ikuti serangkaian instruksi berikut sesuai dengan IDE yang Anda gunakan:
- Unggah Pustaka BME280 dengan uPyCraft IDE
- Unggah Pustaka BME280 dengan Thonny IDE
Unggah Pustaka BME280 dengan uPyCraft IDE
Bagian ini menunjukkan cara mengunggah pustaka menggunakan uPyCraft IDE. Jika Anda menggunakan Thonny IDE, silakan baca bagian berikutnya.
1. Buat file baru dengan menekan tombol New File (1).
2. Salin kode pustaka BME280 ke dalam file tersebut. Kode pustaka BME280 dapat ditemukan [di sini](tautan-kode).
3. Setelah menyalin kode, simpan file dengan menekan tombol Save (2).
4. Beri nama file baru ini `bme280.py` dan tekan OK.
5. Klik tombol Download and Run.
File tersebut akan tersimpan di folder perangkat dengan nama `bme280.py`, seperti yang ditunjukkan pada gambar berikut.
Sekarang, Anda dapat menggunakan fungsi-fungsi pustaka dalam kode Anda dengan mengimpornya.
Unggah Pustaka BME280 dengan Thonny IDE
Jika Anda menggunakan Thonny IDE, ikuti langkah-langkah berikut:
1. Salin kode pustaka ke dalam file baru. Kode pustaka BME280 dapat ditemukan [di sini](tautan-kode).
2. Buka menu File > Save as….
3. Pilih opsi simpan ke "MicroPython device" (Simpan ke perangkat MicroPython):
4. Beri nama file Anda `bme280.py` dan tekan tombol OK:
Selesai. Pustaka telah berhasil diunggah ke papan Anda. Untuk memastikan pengunggahan berhasil, buka File > Save as… dan pilih MicroPython device. File Anda akan terdaftar di sana:
Setelah pustaka berhasil diunggah ke papan ESP, Anda dapat menggunakan fungsinya dalam kode dengan mengimpornya.
Kode Web Server – Suhu, Kelembaban, dan Tekanan dari BME280
Dalam proyek ini, kami akan menampilkan pembacaan sensor suhu, kelembaban, dan tekanan dari BME280 pada sebuah web server yang dapat diakses dalam jaringan lokal Anda. Untuk contoh ini, Anda membutuhkan tiga file:
1. BME280.py: File ini berisi semua metode untuk menggunakan sensor BME280. Ini adalah file yang telah Anda unggah sebelumnya.
2. boot.py: Dijalankan saat perangkat dinyalakan dan mengatur beberapa opsi konfigurasi seperti kredensial jaringan, mengimpor pustaka, mengatur pin, dll.
3. main.py: Ini adalah skrip utama tempat kita akan menangani web server. Skrip ini dieksekusi segera setelah `boot.py`.
Catatan: Adalah praktik yang baik untuk menyertakan file `boot.py` dan `main.py`. Namun, jika Anda lebih suka, Anda dapat menggabungkan semua kode dalam satu file `main.py`.
boot.py
Buat file baru di IDE Anda dengan nama `boot.py` dan salin kode berikut.
try:
import usocket as socket
except:
import socket
from time import sleep
from machine import Pin, I2C
import network
import esp
esp.osdebug(None)
import gc
gc.collect()
import BME280
# ESP32 - Pin assignment
i2c = I2C(scl=Pin(22), sda=Pin(21), freq=10000)
# ESP8266 - Pin assignment
#i2c = I2C(scl=Pin(5), sda=Pin(4), freq=10000)
ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(ssid, password)
while station.isconnected() == False:
pass
print('Connection successful')
print(station.ifconfig())
File ini mengimpor pustaka yang diperlukan, mendefinisikan pin I2C untuk terhubung ke sensor, serta menyambungkan ESP ke jaringan Anda.
Dalam kode, kami menggunakan pin I2C default untuk ESP32:
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=10000)
Jika Anda menggunakan ESP8266, komentari baris sebelumnya dan hapus komentar pada baris berikut:
i2c = I2C(scl=Pin(22), sda=Pin(21), freq=10000)
Kemudian, masukkan kredensial jaringan Anda (SSID dan password Wi-Fi) ke dalam variabel berikut:
ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'
main.py
Di dalam file `main.py` inilah kita akan membuat web server dan menangani permintaan (requests). Salin kode berikut ke file `main.py` Anda.
def web_page():
bme = BME280.BME280(i2c=i2c)
html = """<html><head><meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,"><style>body { text-align: center; font-family: "Trebuchet MS", Arial;}
table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }
th { padding: 12px; background-color: #0043af; color: white; }
tr { border: 1px solid #ddd; padding: 12px; }
tr:hover { background-color: #bcbcbc; }
td { border: none; padding: 12px; }
.sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px;
</style></head><body><h1>ESP with BME280</h1>
<table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>
<tr><td>Temp. Celsius</td><td><span class="sensor">""" + str(bme.temperature) + """</span></td></tr>
<tr><td>Temp. Fahrenheit</td><td><span class="sensor">""" + str(round((bme.read_temperature()/100.0) * (9/5) + 32, 2)) + """F</span></td></tr>
<tr><td>Pressure</td><td><span class="sensor">""" + str(bme.pressure) + """</span></td></tr>
<tr><td>Humidity</td><td><span class="sensor">""" + str(bme.humidity) + """</span></td></tr></body></html>"""
return html
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
while True:
try:
if gc.mem_free() < 102000:
gc.collect()
conn, addr = s.accept()
conn.settimeout(3.0)
print('Got a connection from %s' % str(addr))
request = conn.recv(1024)
conn.settimeout(None)
request = str(request)
print('Content = %s' % request)
response = web_page()
conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.sendall(response)
conn.close()
except OSError as e:
conn.close()
print('Connection closed')
Kode ini membuat server socket yang mengirimkan halaman HTML berisi pembacaan sensor terbaru setiap kali ada permintaan yang diterima di alamat IP ESP32 atau ESP8266.
Pada dasarnya, kita memiliki fungsi bernama `web_page()` yang mengembalikan kode HTML untuk membangun halaman web dengan data sensor terbaru. Teks HTML ini membuat tabel untuk menampilkan pembacaan sensor:
html = """<html><head><meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,"><style>body { text-align: center; font-family: "Trebuchet MS", Arial;}
table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }
th { padding: 12px; background-color: #0043af; color: white; }
tr { border: 1px solid #ddd; padding: 12px; }
tr:hover { background-color: #bcbcbc; }
td { border: none; padding: 12px; }
.sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px;
</style></head><body><h1>ESP with BME280</h1>
<table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>
<tr><td>Temp. Celsius</td><td><span class="sensor">""" + str(bme.temperature) + """</span></td></tr>
<tr><td>Temp. Fahrenheit</td><td><span class="sensor">""" + str(round((bme.read_temperature()/100.0) * (9/5) + 32, 2)) + """F</span></td></tr>
<tr><td>Pressure</td><td><span class="sensor">""" + str(bme.pressure) + """</span></td></tr>
<tr><td>Humidity</td><td><span class="sensor">""" + str(bme.humidity) + """</span></td></tr>
</body></html>"""
Selanjutnya, kita membuat server socket yang mengirimkan HTML saat menerima permintaan. Teks HTML tersebut kemudian disimpan dalam variabel `response`:
response = web_page()
Dan dikirimkan ke klien:
conn.sendall(response)
Demonstrasi Web Server
Unggah semua file sebelumnya ke papan ESP32 atau ESP8266 Anda dengan urutan sebagai berikut:
1. `bme280.py`
2. `boot.py`
3. `main.py`
Setelah kode terunggah, alamat IP ESP32 atau ESP8266 Anda akan ditampilkan di Serial Monitor.
Buka web browser di dalam jaringan lokal Anda dan ketikkan alamat IP ESP (dalam contoh kami, IP-nya adalah `http://192.168.1.71`). Anda akan mendapatkan halaman yang menampilkan pembacaan sensor terbaru seperti pada gambar berikut.
Halaman Web dengan Pembaruan Otomatis (Auto-refresh)
Dengan skrip web server yang disediakan dalam proyek ini, Anda perlu me-refresh halaman web secara manual untuk melihat pembacaan terbaru. Jika Anda menambahkan meta tag berikut di dalam tag `<head></head>` pada HTML, halaman web Anda akan memperbarui diri secara otomatis setiap 10 detik:
<meta http-equiv="refresh" content="10">
Siap Untuk Membuat Proyek Impianmu Menjadi Kenyataan?
Klik di sini untuk chat langsung via WhatsApp dan dapatkan dukungan langsung dari tim ahli kami!





.png)












0 on: "Panduan Lengkap MicroPython ESP32/ESP8266 - Web Server BME280 sebagai Stasiun Cuaca"