Servidor Web Raspberry Pi Pico BME680 com MicroPython

Tempo de leitura: 20 minutes

Neste guia do usuário, aprenderemos a criar um servidor web Raspberry Pi Pico com sensor ambiental BME680, que é usado para medir temperatura ambiente, pressão barométrica, umidade relativa e gás (VOC) ou qualidade do ar interno (IAQ). Aprenderemos como exibir valores de sensores em uma página da Web usando o firmware Raspberry Pi Pico e MicroPython. Este servidor da web funcionará como uma estação meteorológica, pois mostrará as leituras de temperatura, umidade, pressão e IAQ na página da web.

Raspberry Pi Pico não suporta recursos Wi-Fi, portanto, temos que usar um módulo Wi-Fi separado para habilitar a conectividade Wi-Fi. Portanto, vamos fazer a interface e programar o módulo Wi-Fi ESP-01 com o Raspberry Pi Pico para habilitar os recursos Wi-Fi. Usaremos o Thonny IDE para programar o Raspberry Pi Pico com ESP-01 e BME680 em MircoPython. Usaremos comandos AT através da porta serial que é UART para configurar o módulo Wi-Fi ESP-01.

Pré-requisitos

Antes de começarmos esta lição, certifique-se de que você esteja familiarizado e tenha a versão mais recente do Python 3 em seu sistema, tenha configurado o MicoPython no Raspberry Pi Pico e tenha um Ambiente de Desenvolvimento Integrado (IDE) em execução no qual faremos a programação.

 

Introdução ao BME680

O BME680 é um sensor quatro em um de baixa potência, que possui sensores integrados de temperatura, pressão, umidade e detecção de gás. Ele funciona em uma tensão de operação de 1,8-3,8V e se comunica com outros microcontroladores através do protocolo I2C e SPI. Este sensor é usado em áreas como rastreamento da qualidade do ar, indicadores de umidade, tendências climáticas, automação residencial e controle e aprimoramentos de GPS.

Faixa de operação e precisão

Alguns dos principais recursos do BME680 incluem:

  • Medição de temperatura: mede a temperatura ambiente com precisão de ±1,0°C e faixa de operação de -40 a 85°C.
  • Medição de Umidade Relativa: mede a umidade relativa com uma taxa de resposta rápida e uma precisão de ±3% e uma faixa de operação de 0-100%.
  • Medição de pressão: mede a pressão barométrica com precisão absoluta de ±1 hPa e a altitude com precisão de ±1 metro. A faixa de operação de pressão varia de 300-1100 hPa.
PrecisãoFaixa de operação
Temperatura±1.0°C-40 a 85 ºC
Humidade±3%0-100%
Pressão±1 hPa300-1100 hPa
  • Medição de gás: detecta uma ampla gama de gases, incluindo compostos orgânicos voláteis (VOCs), determinando assim a qualidade do ar interno.
  • Devido ao seu tamanho compacto e operação de baixo consumo de energia, é adequado para aplicações móveis, smartwatches e sistemas de navegação.

Em comparação com o BME280, o BME680 é um sensor exclusivo e atualizado no sentido de que contém um sensor MOX (óxido metálico) de tamanho pequeno.

Sensor de gás

O sensor BME680 pode determinar o número de poluentes/COVs no ambiente, como monóxido de carbono, etano, isopreno/2-metil-1,3 butadieno, acetona e etanol. Os COVs são detectados pela adsorção de moléculas de oxigênio na camada de óxido metálico. Sua detecção real é feita pelo princípio de alterar a resistência do sensor MOX. Sempre que o MOX entra em contato com um poluente no ar, a resistência do sensor muda com a concentração dos poluentes presentes. Isso significa que o maior número de poluentes no ar leva a uma resistência significativamente menor do sensor. Da mesma forma, com a menor concentração de VOCs no ar, a resistência do sensor é significativamente maior.

 

Observação: O sensor de gás BME680 não é um sensor de medição de CO2 ou etanol. Ele obtém a ideia relativa do CO2 do VOC no ar, mas não podemos usá-lo para medição direta do CO2.

Como todos os outros sensores de gás, o BME680 também fornece resultados variáveis ​​a cada vez. Para garantir maior precisão, temos que calibrá-lo em relação a uma fonte conhecida e, em seguida, traçar sua curva. Como os níveis de sensibilidade não são constantes durante o uso inicial do sensor, recomenda-se inicialmente executá-lo por quarenta e oito horas, que depois pode ser ajustado para trinta minutos antes de cada uso.

 

Módulos BME680

Vários tipos de sensores BME680 com diferentes tamanhos e números de terminais estão disponíveis no mercado. Mas todos os módulos fornecem dados através de interfaces SPI e I2C e podemos usar o mesmo script MicroPython para obter as leituras dos sensores. Alguns deles são mostrados abaixo:

Você pode escolher o módulo de acordo com sua conveniência e o tipo de microcontrolador ao qual deseja conectá-lo. Neste artigo, usaremos o sensor BME680 com 6 terminais, mostrado abaixo. Possui 4 terminais SPI e 2 I2C.

Diagrama de pinagem

A figura abaixo mostra o sensor BME680 e sua pinagem.

Nome PinoDescrição
VCCEste pino energiza o sensor.
GNDEste é o pino de aterramento comum para alimentação e lógica.
SCLAo usar o protocolo I2C este pino atua como SCL ou pino de clock serial e ao usar o protocolo SPI este pino atua como SCK ou o pino serial para SPI. É a entrada para o chip.
SDAAo usar o protocolo I2C este pino atua como pino SDA ou Serial Data e ao usar o protocolo SPI este pino atua como SDI ou Serial Data In também conhecido como MOSI (‘Master out Slave in’). Isso é usado como a entrada I2C/SPI para o sensor.
SDOEste é o pino SDO (Slave data out) /MISO (Master in slave out) para comunicação SPI. Este pino é usado como saída SPI do sensor.
CSEste é o pino Chip Select usado na comunicação SPI. Atua como uma entrada para o sensor.

 

Interface Raspberry Pi Pico com BME680 e ESP-01

Esta seção mostra como conectar o Raspberry Pi Pico com o sensor BME680 e o ESP-01.

Vamos precisar dos seguintes componentes:

  • Raspberry PI Pico
  • Sensor BME680
  • Módulo ESP-01
  • Fios de conexão
  • Protoboard

 

Raspberry Pi Pico com BME680

Conforme discutido anteriormente, o BME680 oferece duas interfaces, como I2C e SPI. Podemos usar a interface SPI ou I2C para conectar um sensor ao Raspberry Pi Pico. SPI requer quatro pinos para se comunicar. Pelo contrário, o I2C requer dois pinos. Portanto, usaremos a interface I2C para este artigo.

A conexão do BME680 com o Raspberry Pi Pico é muito simples. Temos que conectar o terminal VCC com 3,3V, terra com o terra (terra comum), SCL do sensor com SCL da placa e SDA do sensor com o pino SDA da placa.

Pinos I2C Raspberry Pi Pico

Raspberry Pi Pico tem dois controladores I2C. Ambos os controladores I2C são acessíveis através dos pinos GPIO do Raspberry Pi Pico. A tabela a seguir mostra a conexão dos pinos GPIO com os dois controladores I2C. Cada conexão do controlador pode ser configurada através de vários pinos GPIO conforme mostrado na figura. Mas antes de usar um controlador I2C, você deve configurar no software quais pinos GPIO você deseja usar com um controlador I2C específico.

Controlador I2CPinos GPIO
I2C0 – SDAGP0/GP4/GP8/GP12/GP16/GP20
I2C0 – SCLGP1/GP5/GP9/GP13/GP17/GP21
I2C1 – SDAGP2/GP6/GP10/GP14/GP18/GP26
I2C1 – SCLGP3/GP7/GP11/GP15/GP19/GP27

As conexões entre os dois dispositivos que estamos usando podem ser vistas abaixo.

BME680Raspberry Pi Pico
VCC3.3V
SDAGP2 (I2C1 SDA)
SCLGP3 (I2C1 SCL)
GNDGND

Usamos as mesmas conexões especificadas na tabela acima. No entanto, você também pode usar outras combinações de pinos SDA/SCL, mas lembre-se de alterá-las no script MicroPython.

Raspberry Pi Pico com ESP-01

O módulo ESP-01 é composto por 8 pinos. No entanto, usaremos 5 pinos para conectar com a placa Pi Pico. Estes incluem os pinos VCC, EN, GND, RX e TX. Os pinos RX e TX do módulo serão conectados aos pinos UART da placa Pi Pico. Vamos primeiro dar uma olhada nos pinos UART do Raspberry Pi Pi.

Pinos UART Raspberry Pi Pico

Raspberry Pi Pico contém dois periféricos UART idênticos com FIFOs 32×8 Tx e 32×12 Rx separados.

A tabela a seguir lista os pinos GPIO para ambos os periféricos UART que são expostos nas pinagens da placa de desenvolvimento Raspberry Pi Pico.

Pinos UARTPinos GPIO
UART0-TXGP0/GP12/GP16
UART0-RXGP1/GP13/GP17
UART1-TXGP4/GP8
UART1-RXGP5/GP9

Para este guia, usaremos os pinos UART0-TX e RX.

Siga o diagrama de conexão abaixo para conectar os dois dispositivos.

Raspberry Pi PicoESP-01
3.3VVCC
3.3VEN
GNDGND
GP1 (UART0 RX)TX
GP0 (UART0 TX)RX

Diagrama de conexão Raspberry Pi Pico com BME680 e ESP-01

Usamos as mesmas conexões fornecidas nas duas tabelas acima. Todos os três dispositivos serão comumente aterrados e alimentados com o mesmo pino de 3,3 V do Raspberry Pi Pico.

O diagrama abaixo mostra o diagrama de conexão do Raspberry Pi Pico com BME680 e ESP-01.

Biblioteca MicroPython BME680

Para este projeto, precisaremos da biblioteca MicroPython BME680.

Usaremos o Thonny IDE para programar nosso Raspberry Pi Pico. Certifique-se de ter a versão mais recente do IDE instalada em seu sistema. Abra um novo arquivo no Thonny. Copie a biblioteca abaixo. Salve-o no Raspberry Pi Pico com o nome bme680.py na pasta lib.

bme680.py

# Spaces, comments and some functions have been removed from the original file to save memory
# Original source: https://github.com/adafruit/Adafruit_CircuitPython_BME680/blob/master/adafruit_bme680.py
import time
import math
from micropython import const
from ubinascii import hexlify as hex
try:
  import struct
except ImportError:
  import ustruct as struct
_BME680_CHIPID = const(0x61)
_BME680_REG_CHIPID = const(0xD0)
_BME680_BME680_COEFF_ADDR1 = const(0x89)
_BME680_BME680_COEFF_ADDR2 = const(0xE1)
_BME680_BME680_RES_HEAT_0 = const(0x5A)
_BME680_BME680_GAS_WAIT_0 = const(0x64)
_BME680_REG_SOFTRESET = const(0xE0)
_BME680_REG_CTRL_GAS = const(0x71)
_BME680_REG_CTRL_HUM = const(0x72)
_BME280_REG_STATUS = const(0xF3)
_BME680_REG_CTRL_MEAS = const(0x74)
_BME680_REG_CONFIG = const(0x75)
_BME680_REG_PAGE_SELECT = const(0x73)
_BME680_REG_MEAS_STATUS = const(0x1D)
_BME680_REG_PDATA = const(0x1F)
_BME680_REG_TDATA = const(0x22)
_BME680_REG_HDATA = const(0x25)
_BME680_SAMPLERATES = (0, 1, 2, 4, 8, 16)
_BME680_FILTERSIZES = (0, 1, 3, 7, 15, 31, 63, 127)
_BME680_RUNGAS = const(0x10)
_LOOKUP_TABLE_1 = (2147483647.0, 2147483647.0, 2147483647.0, 2147483647.0, 2147483647.0,
  2126008810.0, 2147483647.0, 2130303777.0, 2147483647.0, 2147483647.0,
  2143188679.0, 2136746228.0, 2147483647.0, 2126008810.0, 2147483647.0,
  2147483647.0)
_LOOKUP_TABLE_2 = (4096000000.0, 2048000000.0, 1024000000.0, 512000000.0, 255744255.0, 127110228.0,
  64000000.0, 32258064.0, 16016016.0, 8000000.0, 4000000.0, 2000000.0, 1000000.0,
  500000.0, 250000.0, 125000.0)
def _read24(arr):
  ret = 0.0
  for b in arr:
    ret *= 256.0
    ret += float(b & 0xFF)
  return ret
class Adafruit_BME680:
  def __init__(self, *, refresh_rate=10):
    self._write(_BME680_REG_SOFTRESET, [0xB6])
    time.sleep(0.005)
    chip_id = self._read_byte(_BME680_REG_CHIPID)
    if chip_id != _BME680_CHIPID:
      raise RuntimeError('Failed 0x%x' % chip_id)
    self._read_calibration()
    self._write(_BME680_BME680_RES_HEAT_0, [0x73])
    self._write(_BME680_BME680_GAS_WAIT_0, [0x65])
    self.sea_level_pressure = 1013.25
    self._pressure_oversample = 0b011
    self._temp_oversample = 0b100
    self._humidity_oversample = 0b010
    self._filter = 0b010
    self._adc_pres = None
    self._adc_temp = None
    self._adc_hum = None
    self._adc_gas = None
    self._gas_range = None
    self._t_fine = None
    self._last_reading = 0
    self._min_refresh_time = 1000 / refresh_rate
  @property
  def pressure_oversample(self):
    return _BME680_SAMPLERATES[self._pressure_oversample]
  @pressure_oversample.setter
  def pressure_oversample(self, sample_rate):
    if sample_rate in _BME680_SAMPLERATES:
      self._pressure_oversample = _BME680_SAMPLERATES.index(sample_rate)
    else:
      raise RuntimeError("Invalid")
  @property
  def humidity_oversample(self):
    return _BME680_SAMPLERATES[self._humidity_oversample]
  @humidity_oversample.setter
  def humidity_oversample(self, sample_rate):
    if sample_rate in _BME680_SAMPLERATES:
      self._humidity_oversample = _BME680_SAMPLERATES.index(sample_rate)
    else:
      raise RuntimeError("Invalid")
  @property
  def temperature_oversample(self):
      return _BME680_SAMPLERATES[self._temp_oversample]
  @temperature_oversample.setter
  def temperature_oversample(self, sample_rate):
    if sample_rate in _BME680_SAMPLERATES:
      self._temp_oversample = _BME680_SAMPLERATES.index(sample_rate)
    else:
      raise RuntimeError("Invalid")
  @property
  def filter_size(self):
    return _BME680_FILTERSIZES[self._filter]
  @filter_size.setter
  def filter_size(self, size):
    if size in _BME680_FILTERSIZES:
      self._filter = _BME680_FILTERSIZES[size]
    else:
      raise RuntimeError("Invalid")
  @property
  def temperature(self):
    self._perform_reading()
    calc_temp = (((self._t_fine * 5) + 128) / 256)
    return calc_temp / 100
  @property
  def pressure(self):
    self._perform_reading()
    var1 = (self._t_fine / 2) - 64000
    var2 = ((var1 / 4) * (var1 / 4)) / 2048
    var2 = (var2 * self._pressure_calibration[5]) / 4
    var2 = var2 + (var1 * self._pressure_calibration[4] * 2)
    var2 = (var2 / 4) + (self._pressure_calibration[3] * 65536)
    var1 = (((((var1 / 4) * (var1 / 4)) / 8192) *
      (self._pressure_calibration[2] * 32) / 8) +
      ((self._pressure_calibration[1] * var1) / 2))
    var1 = var1 / 262144
    var1 = ((32768 + var1) * self._pressure_calibration[0]) / 32768
    calc_pres = 1048576 - self._adc_pres
    calc_pres = (calc_pres - (var2 / 4096)) * 3125
    calc_pres = (calc_pres / var1) * 2
    var1 = (self._pressure_calibration[8] * (((calc_pres / 8) * (calc_pres / 8)) / 8192)) / 4096
    var2 = ((calc_pres / 4) * self._pressure_calibration[7]) / 8192
    var3 = (((calc_pres / 256) ** 3) * self._pressure_calibration[9]) / 131072
    calc_pres += ((var1 + var2 + var3 + (self._pressure_calibration[6] * 128)) / 16)
    return calc_pres/100
  @property
  def humidity(self):
    self._perform_reading()
    temp_scaled = ((self._t_fine * 5) + 128) / 256
    var1 = ((self._adc_hum - (self._humidity_calibration[0] * 16)) -
      ((temp_scaled * self._humidity_calibration[2]) / 200))
    var2 = (self._humidity_calibration[1] *
      (((temp_scaled * self._humidity_calibration[3]) / 100) +
       (((temp_scaled * ((temp_scaled * self._humidity_calibration[4]) / 100)) /
         64) / 100) + 16384)) / 1024
    var3 = var1 * var2
    var4 = self._humidity_calibration[5] * 128
    var4 = (var4 + ((temp_scaled * self._humidity_calibration[6]) / 100)) / 16
    var5 = ((var3 / 16384) * (var3 / 16384)) / 1024
    var6 = (var4 * var5) / 2
    calc_hum = (((var3 + var6) / 1024) * 1000) / 4096
    calc_hum /= 1000
    if calc_hum > 100:
      calc_hum = 100
    if calc_hum < 0:
      calc_hum = 0
    return calc_hum
  @property
  def altitude(self):
    pressure = self.pressure
    return 44330 * (1.0 - math.pow(pressure / self.sea_level_pressure, 0.1903))
  @property
  def gas(self):
    self._perform_reading()
    var1 = ((1340 + (5 * self._sw_err)) * (_LOOKUP_TABLE_1[self._gas_range])) / 65536
    var2 = ((self._adc_gas * 32768) - 16777216) + var1
    var3 = (_LOOKUP_TABLE_2[self._gas_range] * var1) / 512
    calc_gas_res = (var3 + (var2 / 2)) / var2
    return int(calc_gas_res)
  def _perform_reading(self):
    if (time.ticks_diff(self._last_reading, time.ticks_ms()) * time.ticks_diff(0, 1)
        < self._min_refresh_time):
      return
    self._write(_BME680_REG_CONFIG, [self._filter << 2])
    self._write(_BME680_REG_CTRL_MEAS,
      [(self._temp_oversample << 5)|(self._pressure_oversample << 2)])
    self._write(_BME680_REG_CTRL_HUM, [self._humidity_oversample])
    self._write(_BME680_REG_CTRL_GAS, [_BME680_RUNGAS])
    ctrl = self._read_byte(_BME680_REG_CTRL_MEAS)
    ctrl = (ctrl & 0xFC) | 0x01
    self._write(_BME680_REG_CTRL_MEAS, [ctrl])
    new_data = False
    while not new_data:
      data = self._read(_BME680_REG_MEAS_STATUS, 15)
      new_data = data[0] & 0x80 != 0
      time.sleep(0.005)
    self._last_reading = time.ticks_ms()
    self._adc_pres = _read24(data[2:5]) / 16
    self._adc_temp = _read24(data[5:8]) / 16
    self._adc_hum = struct.unpack('>H', bytes(data[8:10]))[0]
    self._adc_gas = int(struct.unpack('>H', bytes(data[13:15]))[0] / 64)
    self._gas_range = data[14] & 0x0F
    var1 = (self._adc_temp / 8) - (self._temp_calibration[0] * 2)
    var2 = (var1 * self._temp_calibration[1]) / 2048
    var3 = ((var1 / 2) * (var1 / 2)) / 4096
    var3 = (var3 * self._temp_calibration[2] * 16) / 16384
    self._t_fine = int(var2 + var3)
  def _read_calibration(self):
    coeff = self._read(_BME680_BME680_COEFF_ADDR1, 25)
    coeff += self._read(_BME680_BME680_COEFF_ADDR2, 16)
    coeff = list(struct.unpack('<hbBHhbBhhbbHhhBBBHbbbBbHhbb', bytes(coeff[1:39])))
    coeff = [float(i) for i in coeff]
    self._temp_calibration = [coeff[x] for x in [23, 0, 1]]
    self._pressure_calibration = [coeff[x] for x in [3, 4, 5, 7, 8, 10, 9, 12, 13, 14]]
    self._humidity_calibration = [coeff[x] for x in [17, 16, 18, 19, 20, 21, 22]]
    self._gas_calibration = [coeff[x] for x in [25, 24, 26]]
    self._humidity_calibration[1] *= 16
    self._humidity_calibration[1] += self._humidity_calibration[0] % 16
    self._humidity_calibration[0] /= 16
    self._heat_range = (self._read_byte(0x02) & 0x30) / 16
    self._heat_val = self._read_byte(0x00)
    self._sw_err = (self._read_byte(0x04) & 0xF0) / 16
  def _read_byte(self, register):
    return self._read(register, 1)[0]
  def _read(self, register, length):
    raise NotImplementedError()
  def _write(self, register, values):
    raise NotImplementedError()
class BME680_I2C(Adafruit_BME680):
  def __init__(self, i2c, address=0x77, debug=False, *, refresh_rate=10):
    self._i2c = i2c
    self._address = address
    self._debug = debug
    super().__init__(refresh_rate=refresh_rate)
  def _read(self, register, length):
    result = bytearray(length)
    self._i2c.readfrom_mem_into(self._address, register & 0xff, result)
    if self._debug:
      print("\t${:x} read ".format(register), " ".join(["{:02x}".format(i) for i in result]))
    return result
  def _write(self, register, values):
    if self._debug:
      print("\t${:x} write".format(register), " ".join(["{:02x}".format(i) for i in values]))
    for value in values:
      self._i2c.writeto_mem(self._address, register, bytearray([value & 0xFF]))
      register += 1

 

Servidor Web MicroPython Script Raspberry Pi Pico BME680 com ESP-01 (Exibir temperatura, umidade, gás e pressão)

import uos
import machine
import utime
from machine import Pin, I2C
from bme680 import *

recv_buf="" # receive buffer global variable

print()
print("Machine: \t" + uos.uname()[4])
print("MicroPython: \t" + uos.uname()[3])


i2c=I2C(1,sda=Pin(2), scl=Pin(3), freq=400000)    #initializing the I2C method 
bme = BME680_I2C(i2c=i2c)
uart0 = machine.UART(0, baudrate=115200)
print(uart0)

def Rx_ESP_Data():
    recv=bytes()
    while uart0.any()>0:
        recv+=uart0.read(1)
    res=recv.decode('utf-8')
    return res
def Connect_WiFi(cmd, uart=uart0, timeout=3000):
    print("CMD: " + cmd)
    uart.write(cmd)
    utime.sleep(7.0)
    Wait_ESP_Rsp(uart, timeout)
    print()

def Send_AT_Cmd(cmd, uart=uart0, timeout=3000):
    print("CMD: " + cmd)
    uart.write(cmd)
    Wait_ESP_Rsp(uart, timeout)
    print()
    
def Wait_ESP_Rsp(uart=uart0, timeout=3000):
    prvMills = utime.ticks_ms()
    resp = b""
    while (utime.ticks_ms()-prvMills)<timeout:
        if uart.any():
            resp = b"".join([resp, uart.read(1)])
    print("resp:")
    try:
        print(resp.decode())
    except UnicodeError:
        print(resp)
    
Send_AT_Cmd('AT\r\n')          #Test AT startup
Send_AT_Cmd('AT+GMR\r\n')      #Check version information
Send_AT_Cmd('AT+CIPSERVER=0\r\n')      #Check version information
Send_AT_Cmd('AT+RST\r\n')      #Check version information
Send_AT_Cmd('AT+RESTORE\r\n')  #Restore Factory Default Settings
Send_AT_Cmd('AT+CWMODE?\r\n')  #Query the Wi-Fi mode
Send_AT_Cmd('AT+CWMODE=1\r\n') #Set the Wi-Fi mode = Station mode
Send_AT_Cmd('AT+CWMODE?\r\n')  #Query the Wi-Fi mode again
#Send_AT_Cmd('AT+CWLAP\r\n', timeout=10000) #List available APs
Connect_WiFi('AT+CWJAP="A32-xX23F","2eXX3A1n"\r\n', timeout=5000) #Connect to AP
Send_AT_Cmd('AT+CIFSR\r\n',timeout=5000)    #Obtain the Local IP Address
Send_AT_Cmd('AT+CIPMUX=1\r\n')    #Obtain the Local IP Address
utime.sleep(1.0)
Send_AT_Cmd('AT+CIPSERVER=1,80\r\n')    #Obtain the Local IP Address
utime.sleep(1.0)
print ('Starting connection to ESP8266...')
while True:
    res =""
    res=Rx_ESP_Data()
    utime.sleep(2.0)
    if '+IPD' in res: # if the buffer contains IPD(a connection), then respond with HTML handshake
        temperature = str(round(bme.temperature, 2)) + ' C'
        humidity = str(round(bme.humidity, 2)) + ' %'
        pressure = str(round(bme.pressure, 2)) + ' hPa'
        gas = str(round(bme.gas/1000, 2)) + ' KOhms'
        print('Temperature:', temperature)
        print('Humidity:', humidity)
        print('Pressure:', pressure)
        print('Gas:', gas)
        print('-------')
        id_index = res.find('+IPD')
        print("resp:")
        print(res)
        connection_id =  res[id_index+5]
        print("connectionId:" + connection_id)
        print ('! Incoming connection - sending webpage')
        uart0.write('AT+CIPSEND='+connection_id+',1440'+'\r\n')  #Send a HTTP response then a webpage as bytes the 108 is the amount of bytes you are sending, change this if you change the data sent below
        utime.sleep(1.0)
        uart0.write('HTTP/1.1 200 OK'+'\r\n')
        uart0.write('Content-Type: text/html'+'\r\n')
        uart0.write('Connection: close'+'\r\n')
        uart0.write(''+'\r\n')
        uart0.write('<!DOCTYPE HTML>'+'\r\n')
        uart0.write('<html><head>'+'\r\n')
        uart0.write('<title>BME680 Web Server</title>'+'\r\n')
        uart0.write('<meta http-equiv=\"refresh\" content=\"10\">'+'\r\n')
        uart0.write('<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\'\r\n')
        uart0.write('<link rel=\"icon\" href=\"data:,\">'+'\r\n')
        uart0.write('<style>'+'\r\n')
        uart0.write('html {font-family: Arial; display: inline-block; text-align: center;}'+'\r\n')
        uart0.write('p {  font-size: 1.2rem;}'+'\r\n')
        uart0.write('body {  margin: 0;}'+'\r\n')
        uart0.write('.topnav { overflow: hidden; background-color: #5c055c; color: white; font-size: 1.7rem; }'+'\r\n')
        uart0.write('.content { padding: 20px; }'+'\r\n')
        uart0.write('.card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }'+'\r\n')
        uart0.write('.cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }'+'\r\n')
        uart0.write('.reading { font-size: 2.8rem; }'+'\r\n')
        uart0.write('.card.temperature { color: #0e7c7b; }'+'\r\n')
        uart0.write('.card.humidity { color: #17bebb; }'+'\r\n')
        uart0.write('.card.pressure { color: hsl(113, 61%, 29%); }'+'\r\n')
        uart0.write('.card.gas { color: #5c055c; }'+'\r\n')
        uart0.write('</style>'+'\r\n')
        uart0.write('</head>'+'\r\n')
        uart0.write('<body>'+'\r\n')
        uart0.write('<div class=\"topnav\">'+'\r\n')
        uart0.write('<h3>Raspberry Pi Pico BME680 WEB SERVER</h3>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"content\">'+'\r\n')
        uart0.write('<div class=\"cards\">'+'\r\n')
        uart0.write('<div class=\"card temperature\">'+'\r\n')
        uart0.write('<h4>Temp. Celsius</h4><p><span class=\"reading\">' + temperature + '</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card humidity\">'+'\r\n')
        uart0.write('<h4>Humidity</h4><p><span class=\"reading\">' + humidity + '</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card pressure\">'+'\r\n')
        uart0.write('<h4>PRESSURE</h4><p><span class=\"reading\">' + pressure +'</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card gas\">' +'\r\n')
        uart0.write('<h4>Gas</h4><p><span class=\"reading\">' + gas + '</p>'+'\r\n')
        uart0.write('</div></div></div>'+'\r\n')
        uart0.write('</body></html>'+'\r\n')
        utime.sleep(4.0)
        Send_AT_Cmd('AT+CIPCLOSE='+ connection_id+'\r\n') # once file sent, close connection
        utime.sleep(4.0)
        recv_buf="" #reset buffer
        print ('Waiting For connection...')

 

Como o Código Funciona?

Começaremos importando o módulo máquina e o módulo uos. Também importaremos a classe I2C e Pin do módulo da máquina. Isso ocorre porque temos que especificar o pino para comunicação I2C. Também importamos o módulo utime para que possamos adicionar um atraso de 10 segundos entre nossas leituras. Além disso, importe a biblioteca bme280 que instalamos anteriormente.

import uos
import machine
import utime
from machine import Pin, I2C
from bme680 import *

Em seguida, imprimiremos as informações sobre nosso sistema operacional atual no terminal Thonny shell. Vamos uos.uname() e imprimir a versão e lançamento do sistema operacional.

print()
print("Machine: \t" + uos.uname()[4])
print("MicroPython: \t" + uos.uname()[3])

Inicializar a comunicação I2C

Em seguida, inicializaremos os pinos I2C GPIO para SCL e SDA, respectivamente. Usamos os pinos I2C1 SCL e I2C0 SDA.

Criamos um método I2C() que recebe quatro parâmetros. O primeiro parâmetro é o canal I2C que estamos usando. O segundo parâmetro especifica o pino I2C GPIO da placa que está conectada à linha SDA. O terceiro parâmetro especifica o pino I2C GPIO da placa que está conectada à linha SCL. O último parâmetro é a conexão de frequência.

Estamos configurando o SCL no pino 3 e o SDA no pino 2.

i2c=I2C(1,sda=Pin(2), scl=Pin(3), freq=400000)

Em seguida, criamos um objeto do BME680 chamado bme e especificamos o protocolo de comunicação I2C para ler os dados do sensor. Isso será usado para acessar as leituras do sensor.

bme = BME680_I2C(i2c=i2c)

Inicializar a comunicação UART

Em seguida, criaremos um objeto uart usando UART() e especificaremos o canal UART como o primeiro parâmetro e a taxa de transmissão como o segundo parâmetro. Estamos usando UART0 neste caso com baud rate 115200 para a comunicação uart. O ESP8266 tem uma taxa de transmissão padrão de 115200, portanto, usaremos a mesma taxa de transmissão aqui para a comunicação UART do Raspberry Pi Pico para criar a sincronização. Além disso, também imprimiremos os detalhes do UART no terminal do shell.

uart0 = machine.UART(0, baudrate=115200)
print(uart0)

Esta função Connect_WiFi() é usada para conectar o ESP8266 com WiFi.

def Connect_WiFi(cmd, uart=uart0, timeout=3000):
    print("CMD: " + cmd)
    uart.write(cmd)
    utime.sleep(7.0)
    Wait_ESP_Rsp(uart, timeout)
    print()

A seguir, definiremos três funções. O primeiro é Rx_ESP_Data(). Isso lê os dados seriais que estão sendo recebidos. Esses dados são decodificados do formato UTF-8 e retornados.

def Rx_ESP_Data():
    recv=bytes()
    while uart0.any()>0:
        recv+=uart0.read(1)
    res=recv.decode('utf-8')
    return res

A segunda função é Send_AT_Cmd(cmd, uart=uart0, timeout=3000). Leva em três parâmetros, o comando AT, o canal UART e o tempo de resposta. Esta função será utilizada para enviar um comando AT para o ESP8266 via uart0. O tempo de resposta é definido para 3 segundos.

def Send_AT_Cmd(cmd, uart=uart0, timeout=3000):
    print("CMD: " + cmd)
    uart.write(cmd)
    Wait_ESP_Rsp(uart, timeout)
    print()

A função Wait_ESP_Rsp(uart=uart0, timeout=3000) aguarda 3 segundos para obter a resposta do ESP8266. Após receber os dados do ESP8266 ele concatena os bytes recebidos e os imprime no terminal shell.

def Wait_ESP_Rsp(uart=uart0, timeout=3000):
    prvMills = utime.ticks_ms()
    resp = b""
    while (utime.ticks_ms()-prvMills)<timeout:
        if uart.any():
            resp = b"".join([resp, uart.read(1)])
    print("resp:")
    try:
        print(resp.decode())
    except UnicodeError:
        print(resp)

Comandos AT

Agora vamos ver a série de comandos AT que enviaremos através do UART0 para o ESP8266.

Send_AT_Cmd('AT\r\n')              #Test AT startup
Send_AT_Cmd('AT+GMR\r\n')          #Check version information
Send_AT_Cmd('AT+CIPSERVER=0\r\n')  #Check version information
Send_AT_Cmd('AT+RST\r\n')          #Check version information
Send_AT_Cmd('AT+RESTORE\r\n')      #Restore Factory Default Settings
Send_AT_Cmd('AT+CWMODE?\r\n')      #Query the Wi-Fi mode
Send_AT_Cmd('AT+CWMODE=1\r\n')     #Set the Wi-Fi mode = Station mode
Send_AT_Cmd('AT+CWMODE?\r\n')      #Query the Wi-Fi mode again
Connect_WiFi('AT+CWJAP="A32-X23F","3X421X1a"\r\n', timeout=5000) #Connect to AP
Send_AT_Cmd('AT+CIFSR\r\n',timeout=5000)    #Obtain the Local IP Address
Send_AT_Cmd('AT+CIPMUX=1\r\n')              #Obtain the Local IP Address
utime.sleep(1.0)
Send_AT_Cmd('AT+CIPSERVER=1,80\r\n')        #Obtain the Local IP Address
utime.sleep(1.0)

AT: Este tipo de comando é usado para testar a função de inicialização do módulo WiFi. A resposta seria ok, contra este comando se tudo estiver ok.

Send_AT_Cmd('AT\r\n') #Test AT startup

AT+GMR: Este tipo de comando AT é usado para verificar a versão do comando AT e usamos a versão SDK do comando AT neste tipo de módulo WIFI.

Send_AT_Cmd('AT+GMR\r\n') #Check version information

AT+CIPSERVER=0: Isso configura o ESP8266 como servidor e define o modo como 0, o que significa excluir servidor (precisa seguir por reinicialização)

Send_AT_Cmd('AT+CIPSERVER=0\r\n')

AT+RST: Este tipo de comando é usado para redefinir o módulo WiFi quando estiver em condições de funcionamento. A resposta seria ok, quando resetar o módulo.

Send_AT_Cmd('AT+RST\r\n')

AT+RESTORE: Este tipo de comando é usado para restaurar as configurações de fábrica significa, quando este comando é inserido, todos os parâmetros são redefinidos automaticamente para os padrões.

Send_AT_Cmd('AT+RESTORE\r\n') #Restore Factory Default Settings

AT+CWMODE?: Este tipo de comando é usado para consultar o modo WiFi do ESP8266.

Send_AT_Cmd('AT+CWMODE?\r\n') #Query the WiFi mode

AT+CWMODE=1: Isso define o modo WiFi do ESP8266 neste caso no modo estação.

Send_AT_Cmd('AT+CWMODE=1\r\n') #Set the WiFi mode = Station mode

AT+CWJAP=”SSID”,”PASSWORD”\r\n’, timeout=TIME_ms: Isso conecta o ESP8266 com um AP cujo SSID e senha são fornecidos, O tempo limite aqui é o tempo de reconexão.

Connect_WiFi('AT+CWJAP="A31-x221","1uX73A1n"\r\n', timeout=5000) #Connect to AP

AT+CIFSR: Este comando obtém o endereço IP local.

Send_AT_Cmd('AT+CIFSR\r\n')

AT+CIPMUX=1: Este comando é usado para habilitar múltiplas conexões (máximo 4)

Send_AT_Cmd('AT+CIPMUX=1\r\n')

AT+CIPSERVER=1:Este comando configura o ESP8266 como servidor.

Send_AT_Cmd('AT+CIPSERVER=1,80\r\n')

loop while

Dentro do loop while, vamos primeiro chamar Rx_ESP_Data() que retorna os dados que o ESP8266 recebe. Isso é salvo na variável ‘res.’

Adicione um atraso de 2 segundos antes de prosseguir.

res =""
res=Rx_ESP_Data()
utime.sleep(2.0)

A seguir, verificaremos se o buffer contém ou não uma conexão IPD. Se isso acontecer, responda com um handshake HTML.

Para obter as leituras do sensor, usaremos o objeto “bme” nos métodos de temperatura, umidade, pressão e gás. Essas leituras são salvas em suas variáveis correspondentes.

Imprima as leituras do sensor BME680 junto com a resposta no terminal shell. Obtenha o ID de conexão e imprima-o também.

if '+IPD' in res: # if the buffer contains IPD(a connection), then respond with HTML handshake
        temperature = str(round(bme.temperature, 2)) + ' C'
        humidity = str(round(bme.humidity, 2)) + ' %'
        pressure = str(round(bme.pressure, 2)) + ' hPa'
        gas = str(round(bme.gas/1000, 2)) + ' KOhms'
        print('Temperature:', temperature)
        print('Humidity:', humidity)
        print('Pressure:', pressure)
        print('Gas:', gas)
        print('-------')
        id_index = res.find('+IPD')
        print("resp:")
        print(res)
        connection_id =  res[id_index+5]
        print("connectionId:" + connection_id)

Então, usando o objeto uart no método write(), enviaremos os bytes para o UART. Primeiro, estamos escrevendo o comando AT: AT+CIPSEND=’ID’, ‘LENGTH’ Isso definirá o comprimento dos dados que serão enviados. Em seguida, após um atraso de 1 segundo, escreveremos o corpo HTML que construirá a página da web na porta serial. Depois disso, fecharemos as múltiplas conexões enquanto estamos enviando o comando AT: AT+CIPCLOSE=’ID’. Em seguida, vamos redefinir o buffer e aguardar a conexão.

print ('! Incoming connection - sending webpage')
        uart0.write('AT+CIPSEND='+connection_id+',1440'+'\r\n')  #Send a HTTP response then a webpage as bytes the 108 is the amount of bytes you are sending, change this if you change the data sent below
        utime.sleep(1.0)
        uart0.write('HTTP/1.1 200 OK'+'\r\n')
        uart0.write('Content-Type: text/html'+'\r\n')
        uart0.write('Connection: close'+'\r\n')
        uart0.write(''+'\r\n')
        uart0.write('<!DOCTYPE HTML>'+'\r\n')
        uart0.write('<html><head>'+'\r\n')
        uart0.write('<title>BME680 Web Server</title>'+'\r\n')
        uart0.write('<meta http-equiv=\"refresh\" content=\"10\">'+'\r\n')
        uart0.write('<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\'\r\n')
        uart0.write('<link rel=\"icon\" href=\"data:,\">'+'\r\n')
        uart0.write('<style>'+'\r\n')
        uart0.write('html {font-family: Arial; display: inline-block; text-align: center;}'+'\r\n')
        uart0.write('p {  font-size: 1.2rem;}'+'\r\n')
        uart0.write('body {  margin: 0;}'+'\r\n')
        uart0.write('.topnav { overflow: hidden; background-color: #5c055c; color: white; font-size: 1.7rem; }'+'\r\n')
        uart0.write('.content { padding: 20px; }'+'\r\n')
        uart0.write('.card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }'+'\r\n')
        uart0.write('.cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }'+'\r\n')
        uart0.write('.reading { font-size: 2.8rem; }'+'\r\n')
        uart0.write('.card.temperature { color: #0e7c7b; }'+'\r\n')
        uart0.write('.card.humidity { color: #17bebb; }'+'\r\n')
        uart0.write('.card.pressure { color: hsl(113, 61%, 29%); }'+'\r\n')
        uart0.write('.card.gas { color: #5c055c; }'+'\r\n')
        uart0.write('</style>'+'\r\n')
        uart0.write('</head>'+'\r\n')
        uart0.write('<body>'+'\r\n')
        uart0.write('<div class=\"topnav\">'+'\r\n')
        uart0.write('<h3>Raspberry Pi Pico BME680 WEB SERVER</h3>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"content\">'+'\r\n')
        uart0.write('<div class=\"cards\">'+'\r\n')
        uart0.write('<div class=\"card temperature\">'+'\r\n')
        uart0.write('<h4>Temp. Celsius</h4><p><span class=\"reading\">' + temperature + '</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card humidity\">'+'\r\n')
        uart0.write('<h4>Humidity</h4><p><span class=\"reading\">' + humidity + '</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card pressure\">'+'\r\n')
        uart0.write('<h4>PRESSURE</h4><p><span class=\"reading\">' + pressure +'</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card gas\">' +'\r\n')
        uart0.write('<h4>Gas</h4><p><span class=\"reading\">' + gas + '</p>'+'\r\n')
        uart0.write('</div></div></div>'+'\r\n')
        uart0.write('</body></html>'+'\r\n')
        utime.sleep(4.0)
        Send_AT_Cmd('AT+CIPCLOSE='+ connection_id+'\r\n') # once file sent, close connection
        utime.sleep(4.0)
        recv_buf="" #reset buffer
        print ('Waiting For connection...')

Criar página da Web (HTML+CSS)

Para construir a página da web, adicionaremos código HTML e, para estilizar, adicionaremos script CSS.

Agora vamos passar por cada linha de código HTML para entender como ele constrói a página da web.

Neste documento HTML, usamos cartões, parágrafos, títulos e tags de título para criar uma página da web. Esta página da web exibe as leituras de temperatura, umidade, pressão e gás do sensor BME680.

HTML é uma linguagem de marcação de hipertexto que é usada para construir páginas da web. Todos os navegadores da Web entendem essa linguagem e podem ler páginas da Web baseadas na linguagem HTML.

Em HTML, colocamos todo o conteúdo de uma página web entre as tags <html> e </html>. A tag <html> mostra o início de uma página da web e a </html> indica o final de uma página da web.

O código HTML inclui principalmente duas partes, como cabeça e corpo. A parte principal contém CSS, scripts, meta tags, links de recursos externos e códigos de estilo. Ele é colocado entre as tags <head> e </head>.

uart0.write('<!DOCTYPE HTML>'+'\r\n')
        uart0.write('<html><head>'+'\r\n')
        uart0.write('<title>BME680 Web Server</title>'+'\r\n')
        uart0.write('<meta http-equiv=\"refresh\" content=\"10\">'+'\r\n')
        uart0.write('<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\'\r\n')
        uart0.write('<link rel=\"icon\" href=\"data:,\">'+'\r\n')
        uart0.write('<style>'+'\r\n')
        uart0.write('html {font-family: Arial; display: inline-block; text-align: center;}'+'\r\n')
        uart0.write('p {  font-size: 1.2rem;}'+'\r\n')
        uart0.write('body {  margin: 0;}'+'\r\n')
        uart0.write('.topnav { overflow: hidden; background-color: #5c055c; color: white; font-size: 1.7rem; }'+'\r\n')
        uart0.write('.content { padding: 20px; }'+'\r\n')
        uart0.write('.card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }'+'\r\n')
        uart0.write('.cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }'+'\r\n')
        uart0.write('.reading { font-size: 2.8rem; }'+'\r\n')
        uart0.write('.card.temperature { color: #0e7c7b; }'+'\r\n')
        uart0.write('.card.humidity { color: #17bebb; }'+'\r\n')
        uart0.write('.card.pressure { color: hsl(113, 61%, 29%); }'+'\r\n')
        uart0.write('.card.gas { color: #5c055c; }'+'\r\n')
        uart0.write('</style>'+'\r\n')
        uart0.write('</head>'+'\r\n')

Vamos começar com o título da página web. A tag <title> indicará o início do título e a tag </title> indicará o final. Entre essas tags, especificaremos “BME680 Web Server” que será exibido na barra de título do navegador.

Neste documento HTML, usamos cartões, parágrafos, títulos e tags de título para criar uma página da web. Esta página da web exibe as leituras de temperatura, umidade e pressão do sensor BME280.

HTML é uma linguagem de marcação de hipertexto que é usada para construir páginas da web. Todos os navegadores da Web entendem essa linguagem e podem ler páginas da Web baseadas na linguagem HTML.

Em HTML, colocamos todo o conteúdo de uma página web entre as tags <html> e </html>. A tag <html> mostra o início de uma página da web e a </html> indica o final de uma página da web.

O código HTML inclui principalmente duas partes, como cabeça e corpo. A parte principal contém CSS, scripts, meta tags, links de recursos externos e códigos de estilo. Ele é colocado entre as tags <head> e </head>.

uart0.write('<!DOCTYPE HTML>'+'\r\n')
uart0.write('<html><head>'+'\r\n')
uart0.write('<title>BME280 Web Server</title>'+'\r\n')
uart0.write('<meta http-equiv=\"refresh\" content=\"10\">'+'\r\n')
uart0.write('<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\'\r\n')
uart0.write('<link rel=\"icon\" href=\"data:,\">'+'\r\n')
uart0.write('<style>'+'\r\n')
uart0.write('html {font-family: Arial; display: inline-block; text-align: center;}'+'\r\n')
uart0.write('p {  font-size: 1.2rem;}'+'\r\n')
uart0.write('body {  margin: 0;}'+'\r\n')
uart0.write('.topnav { overflow: hidden; background-color: #5c055c; color: white; font-size: 1.7rem; }'+'\r\n')
uart0.write('.content { padding: 20px; }'+'\r\n')
uart0.write('.card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }'+'\r\n')
uart0.write('.cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }'+'\r\n')
uart0.write('.reading { font-size: 2.8rem; }'+'\r\n')
uart0.write('.card.temperature { color: #0e7c7b; }'+'\r\n')
uart0.write('.card.humidity { color: #17bebb; }'+'\r\n')
uart0.write('.card.pressure { color: hsl(113, 61%, 29%); }'+'\r\n')
uart0.write('.card.gas { color: #5c055c; }'+'\r\n')
uart0.write('</style>'+'\r\n')
uart0.write('</head>'+'\r\n')

Vamos começar com o título da página web. A tag <title> indicará o início do título e a tag </title> indicará o final. Entre essas tags, especificaremos “BME280 Web Server” que será exibido na barra de título do navegador.

uart0.write('<title>BME680 Web Server</title>'+'\r\n')

Esta meta-tag http-equiv fornece atributos para o cabeçalho HTTP. O atributo http-equiv recebe muitos valores ou informações para simular a resposta do cabeçalho. Neste exemplo, usamos o atributo http-equiv para atualizar o conteúdo da página da Web após cada intervalo de tempo especificado. Os usuários não precisam atualizar a página da Web para obter os valores atualizados do sensor. Essa linha força a página HTML a se atualizar a cada 10 segundos. Além disso, essa metatag garantirá que nosso servidor da Web esteja disponível para todos os navegadores, por exemplo, smartphones, laptops, computadores etc.

uart0.write('<meta http-equiv=\"refresh\" content=\"10\">'+'\r\n')
uart0.write('<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\'\r\n')

Estilizando a página da Web com CSS

CSS é usado para dar estilos a uma página da web. Para adicionar arquivos CSS em tags head, usamos tags <style></style>. Este código CSS estiliza os cartões e a página da web, especificando as cores, fonte, tamanho da fonte, etc.

Este código CSS define o alinhamento do texto, o preenchimento, a margem e a largura das tags do corpo do documento HTML, juntamente com o tamanho da fonte, cor dos cartões, etc. Queremos exibir temperatura, pressão e umidade nos cartões e queremos que eles sejam exibidos no centro da página web.

uart0.write('<style>'+'\r\n')
        uart0.write('html {font-family: Arial; display: inline-block; text-align: center;}'+'\r\n')
        uart0.write('p {  font-size: 1.2rem;}'+'\r\n')
        uart0.write('body {  margin: 0;}'+'\r\n')
        uart0.write('.topnav { overflow: hidden; background-color: #5c055c; color: white; font-size: 1.7rem; }'+'\r\n')
        uart0.write('.content { padding: 20px; }'+'\r\n')
        uart0.write('.card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }'+'\r\n')
        uart0.write('.cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }'+'\r\n')
        uart0.write('.reading { font-size: 2.8rem; }'+'\r\n')
        uart0.write('.card.temperature { color: #0e7c7b; }'+'\r\n')
        uart0.write('.card.humidity { color: #17bebb; }'+'\r\n')
        uart0.write('.card.pressure { color: hsl(113, 61%, 29%); }'+'\r\n')
        uart0.write('.card.gas { color: #5c055c; }'+'\r\n')
        uart0.write('</style>'+'\r\n')

 

Corpo da página da Web HTML

A segunda parte mais importante de um documento HTML é o corpo que vai dentro das tags <body> e </body>. A parte do corpo inclui o conteúdo principal da página da Web, como títulos, imagens, botões, ícones, tabelas, gráficos, etc. Por exemplo, neste servidor da Web baseado em Raspberry Pi Pico BME680 MicroPython, a parte do corpo inclui cabeçalho e quatro cartões exibir as leituras do sensor BME680.

uart0.write('<body>'+'\r\n')
        uart0.write('<div class=\"topnav\">'+'\r\n')
        uart0.write('<h3>Raspberry Pi Pico BME680 WEB SERVER</h3>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"content\">'+'\r\n')
        uart0.write('<div class=\"cards\">'+'\r\n')
        uart0.write('<div class=\"card temperature\">'+'\r\n')
        uart0.write('<h4>Temp. Celsius</h4><p><span class=\"reading\">' + temperature + '</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card humidity\">'+'\r\n')
        uart0.write('<h4>Humidity</h4><p><span class=\"reading\">' + humidity + '</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card pressure\">'+'\r\n')
        uart0.write('<h4>PRESSURE</h4><p><span class=\"reading\">' + pressure +'</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card gas\">' +'\r\n')
        uart0.write('<h4>Gas</h4><p><span class=\"reading\">' + gas + '</p>'+'\r\n')
        uart0.write('</div></div></div>'+'\r\n')
        uart0.write('</body></html>'+'\r\n')

Incluiremos o cabeçalho da nossa página dentro das tags <h3></h3> e será “Raspberry Pi Pico BME680 WEB SERVER”.

uart0.write('<h3>Raspberry Pi Pico BME680 WEB SERVER</h3>'+'\r\n')

Em seguida, incluiremos as seguintes linhas de código para exibir textos e cartões para leituras de temperatura, pressão, umidade e gás.

  uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"content\">'+'\r\n')
        uart0.write('<div class=\"cards\">'+'\r\n')
        uart0.write('<div class=\"card temperature\">'+'\r\n')
        uart0.write('<h4>Temp. Celsius</h4><p><span class=\"reading\">' + temperature + '</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card humidity\">'+'\r\n')
        uart0.write('<h4>Humidity</h4><p><span class=\"reading\">' + humidity + '</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card pressure\">'+'\r\n')
        uart0.write('<h4>PRESSURE</h4><p><span class=\"reading\">' + pressure +'</p>'+'\r\n')
        uart0.write('</div>'+'\r\n')
        uart0.write('<div class=\"card gas\">' +'\r\n')
        uart0.write('<h4>Gas</h4><p><span class=\"reading\">' + gas + '</p>'+'\r\n')
        uart0.write('</div></div></div>'+'\r\n')

Demonstração

Depois de copiar o código a seguir em um novo arquivo, clique no ícone ‘Salvar’ para salvar o código do programa em seu PC.

Depois de salvar o código, pressione o botão Executar para fazer o upload do código para sua placa. Antes de fazer o upload do código, certifique-se de que a placa correta esteja selecionada.

No terminal shell do seu IDE, você poderá visualizar o endereço IP depois que uma conexão bem-sucedida for estabelecida:

Agora, abra seu navegador da Web em seu laptop ou celular e digite o endereço IP que encontramos na última etapa. Assim que você digitar o endereço IP no seu navegador da Web e pressionar enter, o servidor da Web Raspberry Pi Pico receberá uma solicitação HTTP.

Você verá a página da web com os valores de temperatura mais recentes em seu navegador da web:

A página da Web visualizada de um telefone celular terá a seguinte aparência: