برد Esp32 میکروپایتون Micropython

پروژه ایستگاه هواشناسی با استفاده از BME280 و میکروپایتون در برد ESP32

نوشته شده توسط CiferTech

در پروژه ایستگاه هواشناسی میکروپایتون طراحی وب سرور با استفاده از سنسور BME280 با برد توسعه ESP32 و MicroPython را یاد می گیریم. BME280 برای اندازه گیری دما بر حسب سلسیوس، همچنین بر حسب فارنهایت، رطوبت و فشار استفاده می شود. این وب سرور به عنوان یک ایستگاه هواشناسی عمل می کند زیرا دما، رطوبت و فشار را در صفحه وب با استفاده از MicroPython نشان می دهد. ما از MicroPython برای ساخت یک وب‌سرور EP32 استفاده خواهیم کرد که از طریق هر دستگاهی که دارای مرورگر وب است قابل دسترسی باشد. در این پروژه برای کدنویسی از نرم‌افزار upycraft استفاده خواهیم کرد. در ادامه این آموزش با مرجع تخصصی آردوینو به زبان فارسی، دیجی اسپارک همراه باشید.

ایستگاه هواشناسی با bme280 و میکروپایتون - دیجی اسپارک

 


سنسور BME280


سنسور BME280 برای اندازه گیری های مربوط به دمای محیط، فشار هوا و رطوبت نسبی استفاده می شود. این سنسور در برنامه های تحت وب و تلفن همراه استفاده می شود. این سنسور از I2C یا SPI برای ارتباط داده ها با میکروکنترلرها استفاده می کند. اگرچه چندین نسخه مختلف از BME280 در بازار موجود است، نسخه ای که ما در مورد آن مطالعه خواهیم کرد از پروتکل ارتباطی I2C استفاده می کند.

سنسور bme280 در ساخت ایستگاه هواشناسی - دیجی اسپارک

 


تراشه ESP32


ESP32 می تواند به عنوان یک سیستم کامل مستقل یا به عنوان یک دستگاه MCU عمل کند و بار اضافی ارتباطات را در پردازنده اصلی برنامه کاهش دهد. ESP32 می تواند با سیستم های دیگر ارتباط برقرار کند تا از طریق SPI عملکرد Wi-Fi و بلوتوث را ارائه دهد. ESP32 با سوئیچ های آنتن داخلی، RF balun، تقویت کننده قدرت، تقویت کننده دریافت، فیلترها و ماژول های مدیریت توان یکپارچه شده است. ESP32 با حداقل الزامات برد مدار چاپی (PCB) ، عملکرد و تطبیق پذیری بی نظیری را به برنامه های شما اضافه می کند. برد ESP32 نسل پیشرفته ESP8266 است. یکی از تفاوت‌های آن بلوتوث داخلی‌اش می‌باشد. همچنین دارای هسته وایفای ۲,۴ گیگا هرتزی و بلوتوث داخلی تولید شده با تکنولوژی ۴۰ نانومتری شرکت TSMC می‌باشد. این ماژول دارای بهترین پرفورمنس در مصرف انرژی می‌باشد یعنی با کمترین مصرف انرژی بهترین نتیجه را برای ما به همراه دارد. اگر بخواهیم دقیق‌تر به این برد نگاه کنیم باید بگوییم که این یک chip است که پلتفرم NodeMCU در اون پیاده سازی شده که به این نوع چیپ ها System on a chip microcontrollers هم گفته می‌شود.

 


قطعات مورد نیاز


برد ESp32

سنسور BME280

وسایل لازم برای ساخت ایستگاه هواشناسی میکروپایتون - دیجی اسپارک

 


شرح پروژه ایستگاه هواشناسی میکروپایتون


در پروژه ایستگاه هواشناسی میکروپایتون با BME280 و برد ESP32 به عنوان یک Master عمل می کند، و سنسور BME280 به عنوان یک Slave به دلیل اینکه یک دستگاه خارجی است، به عنوان یک Slave عمل می کند. بردهای توسعه ESP با حسگر BME280 از طریق پروتکل I2C برای دما، فشار هوا و رطوبت نسبی ارتباط برقرار می کنند. و در ادامه با راه‌اندازی یک وب سرور مقادیر مورد نظر را در شبکه لوکال نمایش می‌دهیم.

 


شماتیک پروژه ایستگاه هواشناسی میکروپایتون


پایه I2C در ESP32 برای SDA به GPIO21 و برای SCL به GPIO22 متصل می‌شود. اتصال BME280 با بردهای ESP بسیار آسان است. پین VCC را باید با ۳٫۳ ولت، GND را با GND و SCL سنسور را با SCL ماژول و SDA سنسور را با پایه SDA برد های ESP متصل کنیم.
اتصالات پروژه ایستگاه هواشناسی میکروپایتون - دیجی اسپارک

 


کتابخانه BME280 در میکروپایتون


در MicroPython بطور پیفرض کتابخانه BME280 وجود ندارد. اما MicroPyhon I2C API برای ESP32 ارائه می‌شود، که می تواند برای خواندن مقادیر دما، رطوبت و فشار از سنسور BME280 استفاده شود. کد های زیر را برای پیاده سازی پروژه ایستگاه هواشناسی میکروپایتون در نرم‌افزار خود در یک فایل جدید با نام BME280.py کپی کنید، در ادامه این کد ها همراه با با کد اصلی در برد آپلود خواهد شد.
from machine import I2C
import time

BME280_I2CADDR = 0x76

BME280_OSAMPLE_1 = 1
BME280_OSAMPLE_2 = 2
BME280_OSAMPLE_4 = 3
BME280_OSAMPLE_8 = 4
BME280_OSAMPLE_16 = 5


BME280_REGISTER_DIG_T1 = 0x88  
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)) +
         ۱۶۳۸۴) >> 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)

 


کد های پروژه ایستگاه هواشناسی


در این قسمت نحوه نمایش خواندن و نمایش مقادیر بدست آمده از BME280 را در وب سرور یاد خواهیم گرفت. در ادامه نیاز به ساخت فایل دیگری داریم، به همین ترتیب، یک فایل با نام boot.py را نیز آپلود خواهیم کرد. این مورد شامل تمام پیکربندی‌های اصلی مانند راه‌اندازی پین‌ها، I2C GPIO و وارد کردن کتابخانه‌های مرتبط دیگر است. و در نهایت فایل main.py را آپلود می کنیم که حاوی کد نهایی ما برای پیکربندی وب سرور است که بعد از boot.py اجرا می شود.

کد های فایل boot.py

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())

 

  • مقادیری که در این باید تغییر کند میبوط به اطلاعات شبکه وای فای شماست که در بخش کد مدنظر موارد ssid و pass را وارد می‌کنیم.
ssid = 'Enter your SSID name'
password = 'Enter your SSID password here'

 

کد های فایل main.py

def web_page():
  bme = BME280.BME280(i2c=i2c)
  
  html = """<html>
<head>
  <title>BME280 Web Server</title>
  <meta http-equiv="refresh" content="10">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="icon" href="data:,">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    p {  font-size: 1.2rem;}
    body {  margin: 0;}
    .topnav { overflow: hidden; background-color: #0e7c7b; color: white; font-size: 1.7rem; }
    .content { padding: 20px; }
    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }
    .cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
    .reading { font-size: 2.8rem; }
    .card.temperature { color: #0e7c7b; }
    .card.humidity { color: #17bebb; }
    .card.pressure { color: #3fca6b; }
  </style>
</head>
<body>
  <div class="topnav">
    <h3>BME280 WEB SERVER</h3>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card temperature">
        <h4><i class="fas fa-thermometer-half"></i>Temp. Celsius</h4><p><span class="reading">""" + str(bme.temperature) + """</p>
      </div>
      <div class="card temperature">
        <h4><i class="fas fa-thermometer-half"></i> Temp. Fahrenheit</h4><p><span class="reading">""" + str(round((bme.read_temperature()/100.0) * (9/5) + 32, 2))  + """F</p>
      </div>
      <div class="card pressure">
        <h4><i class="fas fa-angle-double-down"></i> PRESSURE</h4><p><span class="reading">""" + str(bme.pressure) + """</p>
      </div>
      <div class="card humidity">
        <h4><i class="fas fa-tint"></i> HUMIDITY</h4><p><span class="reading">""" + str(bme.humidity) + """</p>
      </div>
    </div>
  </div>
</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')

 


نتیجه نهایی


در نهایت پس از آپلود کد های پروژه ایستگاه هواشناسی میکروپایتون، فایل های ساخته شده برای کتابخانه در ترمینال upycraft اگر تمامی مراحل به درستی انجام شده باشد و اتصال وای‌فای برقرار باشد یک IP که آدرس وب سرور مربوط پروژه ایستگاه هواشناسی شما است نمایش داده خواهد شد، که با جستجو این ip آدرس در مرورگر خود به اطلاع مربوط به سنسور دسترسی پیدا خواهید کرد. به منظور تغییر ظاهر وب‌سرور قادر خواهید بود با تغییر کد های مربوط به صفحه وب در کد های اصلی پروژه تغییرات مد نظر را اعمال کنید.

ایستگاه هواشناسی میکروپایتون سنسور bme280 - دیجی اسپارک

 


کلام آخر با سایفر


در نهایت پس آپلود تمام کدها اکنون مرورگر وب خود را باز کرده و آدرس IP را که در در ترمینال نرم‌افزار نمایش داده می‌شود، تایپ کنید. در ادامه بعد از اینکه آدرس IP را در مرورگر خود تایپ و سرچ کردید. وب سرور ESP32 یک درخواست HTTP دریافت خواهد کرد. تابع صفحه وب فراخوانی می شود. در نهایت صفحه وب با تمام مقادیر مورد نظر را مشاهده خواهید کرد. از پروژه ایستگاه هواشناسی میکروپایتون میتوان برای سناریو ها و مکان های مختلف به منظور مانیتورینگ استفاده کرد.
ایستگاه هواشناسی میکروپایتون و Nodemcu esp32 - دیجی اسپارک

چنانچه در مراحل راه اندازی و انجام این پروژه با مشکل مواجه شدید، بدون هیچ نگرانی در انتهای همین پست، به صورت ثبت نظر سوالتان را مطرح کنید. من در سریع‌ترین زمان ممکن پاسخ رفع مشکل شما را خواهم داد. همچنین اگر ایرادی در کدها و یا مراحل اجرایی وجود دارند می‌توانید از همین طریق اطلاع رسانی کنید.

 

در پایان نظرات و پیشنهادات خود را با ما درمیان بگذارید و با اشتراک گذاری این آموزش در شبکه های اجتماعی , از وبسایت دیجی اسپارک حمایت کنید.

 

درباره نویسنده

CiferTech

فقط 10 نوع آدم در این دنیا وجود داره، اونی که باینری میفهمه و اونی که باینری نمیفهمه! ^-^

~ اینستاگرام: CiferTech

تبادل نظر و رفع عیب با ثبت دیدگاه