Raspberry Pi Pico DS18B20 Web Server (Weather Station)

In this user guide, we will learn to create a Raspberry Pi Pico web server using a DS18B20 sensor and MicroPython which displays the values of temperature in Celsius and Fahrenheit units. It will act as a weather station as it will show temperature readings on the web page using MicroPython. We will use MicroPython firmware to build a responsive Raspberry Pi Pico web server that can be accessed through any device which has a web browser, and the device should be connected to your local area network. That means the device should be connected to the same network to which the board is connected.

Raspberry Pi Pico Web Server with DS18B20 and ESP8266

Raspberry Pi Pico does not support Wi-Fi capabilities hence we have to use a separate Wi-Fi module to enable Wi-Fi connectivity. Therefore, we will interface and program ESP-01 Wi-Fi module with Raspberry Pi Pico to enable Wi-Fi features. We will use Thonny IDE to program Raspberry Pi Pico with ESP-01 and BME680 in MircoPython. We will use AT commands through the serial port that is UART to configure the ESP-01 Wi-Fi module.

Prerequisites

Before we start this lesson make sure you are familiar with and have the latest version Python 3 in your system, have set up MicoPython in Raspberry Pi Pico, and have a running Integrated Development Environment(IDE) in which we will be doing the programming. We will be using the same Thonny IDE as we have done previously when we learned how to blink and chase LEDs in micro-python. If you have not followed our previous tutorial, you check here:

If you are using uPyCraft IDE, you can check this getting started guide:

Recommended Readings: 

DS18B20 Introduction

It is a temperature sensor that is single wire programmable in nature. It is widely used to measure the temperature of chemical solutions and substances which are present in a hard environment. One of the advantages of using this sensor is that we only require a single pin of our Raspberry Pi Pico boards to transfer data. Thus, it is extremely convenient to use with the micro-controller as we can measure multiple temperatures by using the least number of pins on our development board.

The table below shows some key characteristics of the ds18b120 sensor.

FeatureValue
Operating Voltage3V-5V
Temperature Range-55°C to +125°C
Accuracy±0.5°C
Output Resolution9bit to 12bit
Key Characteristics of DS18B20

Pinout Diagram

A waterproof version of this sensor is also available in the market. The following figures show the pinout of the DS18B20 sensors.

ds18b20 waterproof pinout diagram
Pin out of waterproof DS18B20

The following diagram shows the pinout of normal DS18B20 temperature sensor.

ds18b20 pinout diagram

The table below lists the pin configurations:

PinDescription
VCCThis is the pin that powers up the sensor. 3.3V for Raspberry Pi Pico boards
DataThis pin gives the temperature value
GroundThis pin is connected to ground
Pin Configuration details DS18B20

This temperature sensor also comes in a single package module which contains a sensor and a pull-up resistor. If you are using a module, you do not need to connect an external 4.7K ohm resistor. Because the module already has an onboard pull-up resistor.

ds18b20 module
Ds18b20 Module

DS18B20 Parasite vs Normal Mode

The DS18B20 sensor can be powered in two different modes.

Normal Mode: The sensor is powered through an external source through the VDD pin and 4.7K ohm pull-up resistor.

Parasite Mode: The sensor obtains the power from its own data line. Hence, no external power supply is required.

Interfacing Raspberry Pi Pico with DS18B20 and ESP-01

Raspberry Pi Pico with ds18b20 and ESP-01

This section shows how to connect Raspberry Pi Pico with DS18B20 sensor and ESP-01.

We will require the following components:

  • Raspberry Pi Pico
  • DS18B20 Sensor
  • 4.7k ohm resistor
  • ESP-01 Module
  • Connecting Wires
  • Breadboard

Raspberry Pi Pico with DS18B20

We will power the DS10B20 sensor in normal mode. Therefore its VCC pin will be connected with the 3.3V pin of Raspberry Pi Pico board through a 4.7k ohm resistor.

The DS18B20 sensor has three terminals which we saw above in the pinout. The first terminal is grounded with the Raspberry Pi Pico board. The data line of the sensor, which is the middle terminal, is connected through GP16 through a pull-up resistor of 4.7k-ohm. We can choose any other GPIO pin as well. The third terminal is powered by 3.3V from the Raspberry Pi Pico.

Raspberry Pi PicoDS18B20
GNDGND
GP16Data
3.3VVCC

You may also like to read:

You can use any other GPIO pin of Raspberry Pi Pico to connect with the data pin as well. You can refer to this post to know more about Raspberry Pi Pico GPIO pins:

Raspberry Pi Pico with ESP-01

The ESP-01 module consists of 8 pins. However, we will use 5 pins to connect with the Pi Pico board. These include the VCC, EN, GND, RX, and TX pins. RX and TX pins of the module will be connected with the UART pins of the Pi Pico board. Let us first have a look at the Raspberry Pi Pi UART Pins.

Raspberry Pi Pico UART Pins

Raspberry Pi Pico contains two identical UART peripherals with separate 32×8 Tx and 32×12 Rx FIFOs.

Raspberry Pi Pico pinout diagram

The following table lists the GPIO pins for both UART peripherals which are exposed on Raspberry Pi Pico development board pinouts. 

UART PinsGPIO Pins
UART0-TXGP0/GP12/GP16
UART0-RXGP1/GP13/GP17
UART1-TXGP4/GP8
UART1-RXGP5/GP9

For this guide we will use UART0-TX and RX pins.

Follow the connection diagram below to connect the two devices.

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

Connection Diagram Raspberry Pi Pico with DS18B20 and ESP-01

We have used the same connections as given in the two tables above. All three devices will be commonly grounded and will be powered with the same 3.3V pin of Raspberry Pi Pico.

The diagram below shows the connection diagram of Raspberry Pi Pico with DS18B20 and ESP-01.

Raspberry Pi Pico with ds18b20 and ESP-01 connection diagram
Raspberry Pi Pico with ds18b20 and ESP-01 connection diagram

You may like to read:

Installing DS18B20 Libraries

For this project, we will require two libraries: ds18x20.py and onewire.py. Copy both of these libraries and save them in your Raspberry Pi Pico with the respective file names. Open a new file in Thonny. Copy the libraries given below. Save them to Raspberry Pi Pico with names ds18x20.py and onewire.py under the lib folder.

ds18x20.py

# DS18x20 temperature sensor driver for MicroPython.
# MIT license; Copyright (c) 2016 Damien P. George

from micropython import const

_CONVERT = const(0x44)
_RD_SCRATCH = const(0xBE)
_WR_SCRATCH = const(0x4E)


class DS18X20:
    def __init__(self, onewire):
        self.ow = onewire
        self.buf = bytearray(9)

    def scan(self):
        return [rom for rom in self.ow.scan() if rom[0] in (0x10, 0x22, 0x28)]

    def convert_temp(self):
        self.ow.reset(True)
        self.ow.writebyte(self.ow.SKIP_ROM)
        self.ow.writebyte(_CONVERT)

    def read_scratch(self, rom):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_RD_SCRATCH)
        self.ow.readinto(self.buf)
        if self.ow.crc8(self.buf):
            raise Exception("CRC error")
        return self.buf

    def write_scratch(self, rom, buf):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_WR_SCRATCH)
        self.ow.write(buf)

    def read_temp(self, rom):
        buf = self.read_scratch(rom)
        if rom[0] == 0x10:
            if buf[1]:
                t = buf[0] >> 1 | 0x80
                t = -((~t + 1) & 0xFF)
            else:
                t = buf[0] >> 1
            return t - 0.25 + (buf[7] - buf[6]) / buf[7]
        else:
            t = buf[1] << 8 | buf[0]
            if t & 0x8000:  # sign bit set
                t = -((t ^ 0xFFFF) + 1)
            return t / 16

onewire.py

# 1-Wire driver for MicroPython
# MIT license; Copyright (c) 2016 Damien P. George

import _onewire as _ow


class OneWireError(Exception):
    pass


class OneWire:
    SEARCH_ROM = 0xF0
    MATCH_ROM = 0x55
    SKIP_ROM = 0xCC

    def __init__(self, pin):
        self.pin = pin
        self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP)

    def reset(self, required=False):
        reset = _ow.reset(self.pin)
        if required and not reset:
            raise OneWireError
        return reset

    def readbit(self):
        return _ow.readbit(self.pin)

    def readbyte(self):
        return _ow.readbyte(self.pin)

    def readinto(self, buf):
        for i in range(len(buf)):
            buf[i] = _ow.readbyte(self.pin)

    def writebit(self, value):
        return _ow.writebit(self.pin, value)

    def writebyte(self, value):
        return _ow.writebyte(self.pin, value)

    def write(self, buf):
        for b in buf:
            _ow.writebyte(self.pin, b)

    def select_rom(self, rom):
        self.reset()
        self.writebyte(self.MATCH_ROM)
        self.write(rom)

    def scan(self):
        devices = []
        diff = 65
        rom = False
        for i in range(0xFF):
            rom, diff = self._search_rom(rom, diff)
            if rom:
                devices += [rom]
            if diff == 0:
                break
        return devices

    def _search_rom(self, l_rom, diff):
        if not self.reset():
            return None, 0
        self.writebyte(self.SEARCH_ROM)
        if not l_rom:
            l_rom = bytearray(8)
        rom = bytearray(8)
        next_diff = 0
        i = 64
        for byte in range(8):
            r_b = 0
            for bit in range(8):
                b = self.readbit()
                if self.readbit():
                    if b:  # there are no devices or there is an error on the bus
                        return None, 0
                else:
                    if not b:  # collision, two devices with different bit meaning
                        if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i):
                            b = 1
                            next_diff = i
                self.writebit(b)
                if b:
                    r_b |= 1 << bit
                i -= 1
            rom[byte] = r_b
        return rom, next_diff

    def crc8(self, data):
        return _ow.crc8(data)

MicroPython Script Raspberry Pi Pico DS18B20 Web Server with ESP-01 (Weather Station)

import uos
import utime
import machine, onewire, ds18x20

recv_buf="" # receive buffer global variable

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

ds_pin = machine.Pin(16)
ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin))
roms = ds_sensor.scan()

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
Connect_WiFi('AT+CWJAP="A1601","123456789104"\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
        print('Found DS devices')
        print('Temperature')
        ds_sensor.convert_temp()
        utime.sleep(1)
        for rom in roms:
          temperature_Celsius = round(ds_sensor.read_temp(rom), 2)
          temperature_Fahrenheit = round((ds_sensor.read_temp(rom)*(9/5) + 32.0), 2)
          print(temperature_Celsius) 
          print( temperature_Fahrenheit)
          print('__________________')
        utime.sleep(3)
        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+',1569'+'\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')
        html ='<html> <head> <meta http-equiv=\"refresh\" content=\"10\"> <title>Raspberry Pi Pico Web Server</title> <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;} .top_nav { overflow: hidden; background-color: #da0a0a; color: white; font-size: 1rem; } .content { padding: 30px; } .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); } .cards { max-width: 800px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } .value { font-size: 3rem; } .symbol { font-size: 2rem; } </style> </head> <body> <div class=\"top_nav\"> <h1>Raspberry Pi Pico DS18B20 Web Server</h1> </div> <div class=\"content\"> <div class=\"cards\"> <div class=\"card\"> <p><i class=\"fas fa-thermometer-half fa-3x\" style=\"color:#da0a0a;\"></i><span class=\"symbol\">Temperature</span></p><p><span class=\"value\"><span id=\"temperature_Celsius\">' + str(temperature_Celsius) + '</span> &deg;C</span></p> </div> <div class=\"card\"> <p><i class=\"fas fa-thermometer-half fa-3x\" style=\"color:#da0a0a;\"></i> <span class=\"symbol\">Temperature</span></p><p><span class=\"value\"><span id=\"temperature_Fahrenheit\">' + str(temperature_Fahrenheit)+ '</span> &deg;F</span></p> </div> </div> </div> </body> </html>'
        uart0.write(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)
        res="" #reset buffer
        print ('Waiting For connection...')

How the Code Works?

We will start by importing the machine module and the uos module. We also import the utime module so that we will be able to add a delay of 10 seconds in between our readings. Also, import one wire and ds18x20 libraries that we just uploaded to our board.

import uos
import utime
import machine, onewire, ds18x20

Then, we will print the information about our current operating system in the Thonny shell terminal. We will uos.uname() and print the operating system version and release.

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

Next, we will create an instance of Pin class with an object name of ds_pin and set GP16 as the DS18B20 data line pin.

ds_pin = machine.Pin(16)
ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin))

The scan() method scans all DS18B20 sensors connected to the ds_sensor pin and saves the 64-bit address of each sensor in a list variable that is “roms”. Later, we will use these addresses to read temperature from the sensor.

roms = ds_sensor.scan()

Initialize UART Communication

Then we will create an uart object by using UART() and specify the UART channel as the first parameter and the baud rate as the second parameter. We are using UART0 in this case with baud rate 115200 for the uart communication. ESP8266 has a default baud rate of 115200 hence we will use the same baud rate here for Raspberry Pi Pico UART communication in order to create synchronization. Moreover we will also print the UART details in the shell terminal.

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

This Connect_WiFi() function is used to connect ESP8266 with 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()

Next, we will define three functions. The first one is Rx_ESP_Data(). This reads the serial data being received. This data is decoded from UTF-8 format and returned.

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

The second function is Send_AT_Cmd(cmd, uart=uart0, timeout=3000). It takes in three parameters, the AT command, the UART channel and the response time. This function will be used to it send an AT command to ESP8266 via uart0. The response time is set to 3 seconds.


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

The Wait_ESP_Rsp(uart=uart0, timeout=3000) function waits for 3 seconds to get the response from ESP8266. After receiving the data from ESP8266 it concatenates the received bytes and prints them on the shell terminal.

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)

AT Commands

Now let us look at the series of AT commands that we will send through UART0 to 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="YOUR_SSID","YOUR_PASSWORD"\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: This type of command is used to test the startup function of WiFi module. The response would be ok, against this command if everything is ok.

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

AT+GMR : This type of AT command is used to check the version of AT command and we used SDK version of AT command in this type of WIFI module.

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

AT+CIPSERVER=0: This configures the ESP8266 as server and sets the mode as 0 which means delete server (need to follow by restart)

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

AT+RST: This type of command is used for reset the WiFi module when it is in working condition. The response would be ok, when reset the module.

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

AT+RESTORE: This type of command is used to restore factory settings means, when this command is entered then all the parameters are reset automatically to default one’s.

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

AT+CWMODE? : This type of command is used to query the WiFi mode of ESP8266.

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

AT+CWMODE=1: This sets the WiFi mode of ESP8266 in this case in station mode.

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

AT+CWJAP=”SSID”,”PASSWORD”\r\n’, timeout=TIME_ms : This connects the ESP8266 with an AP whose SSID and password are given, The timeout here is the reconnection time.

Connect_WiFi('AT+CWJAP="YOUR_SSID","YOUR_PASSWORD"\r\n', timeout=5000) #Connect to AP

AT+CIFSR: This command obtains the local IP address.

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

AT+CIPMUX=1:This command is used to enable multiple connections (maximum 4)

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

AT+CIPSERVER=1: This command configures ESP8266 as server.

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

while loop

Inside the while loop, we will first call Rx_ESP_Data() which returns the data that the ESP8266 receives. This is saved in the variable ‘res.’

Add a delay of 2 seconds before proceeding further.

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

Next, we will check if the buffer contains an IPD connection or not. If it does then, respond with an HTML handshake.

To get the sensor readings, we will call the object(ds_sensor) on convert_temp() method before reading the temperature from the sensor using its unique address. To read the temperature in Celsius, we use the read_temp() procedure on the ds_sensor object and pass an address stored in the roms list. The DS18B20 temperature sensor provides data output in float data type. We will round off the reading to 2 decimal places and store it as a string. Moreover, we will do some calculations to convert the Celsius reading to Fahrenheit as well. Both temperature readings are saved in their corresponding variables.

These DS18B20 sensor readings along with the response will be printed in the shell terminal. Additionally, we obtain the connection ID and print it as well.

    if '+IPD' in res: # if the buffer contains IPD(a connection), then respond with HTML handshake
        print('Found DS devices')
        print('Temperature')
        ds_sensor.convert_temp()
        utime.sleep(1)
        for rom in roms:
          temperature_Celsius = round(ds_sensor.read_temp(rom), 2)
          temperature_Fahrenheit = round((ds_sensor.read_temp(rom)*(9/5) + 32.0), 2)
          print(temperature_Celsius) 
          print( temperature_Fahrenheit)
          print('__________________')
        utime.sleep(3)
        id_index = res.find('+IPD')
        print("resp:")
        print(res)
        connection_id =  res[id_index+5]
        print("connectionId:" + connection_id)

Then by using the uart object on the write() method, we will send the bytes to the UART. First, we are writing the AT command: AT+CIPSEND=’ID’, ‘LENGTH’ This will set the length of the data that will be sent. Next after a delay of 1 second, we will write the HTML script that will build the web page to the serial port. After that, we will close the multiple connections as we are sending the AT command: AT+CIPCLOSE=’ID’. Then we will reset the buffer and wait for the connection.

print ('! Incoming connection - sending webpage')
        uart0.write('AT+CIPSEND='+connection_id+',1569'+'\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')
        html ='<html> <head> <meta http-equiv=\"refresh\" content=\"10\"> <title>Raspberry Pi Pico Web Server</title> <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;} .top_nav { overflow: hidden; background-color: #da0a0a; color: white; font-size: 1rem; } .content { padding: 30px; } .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); } .cards { max-width: 800px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } .value { font-size: 3rem; } .symbol { font-size: 2rem; } </style> </head> <body> <div class=\"top_nav\"> <h1>Raspberry Pi Pico DS18B20 Web Server</h1> </div> <div class=\"content\"> <div class=\"cards\"> <div class=\"card\"> <p><i class=\"fas fa-thermometer-half fa-3x\" style=\"color:#da0a0a;\"></i><span class=\"symbol\">Temperature</span></p><p><span class=\"value\"><span id=\"temperature_Celsius\">' + str(temperature_Celsius) + '</span> &deg;C</span></p> </div> <div class=\"card\"> <p><i class=\"fas fa-thermometer-half fa-3x\" style=\"color:#da0a0a;\"></i> <span class=\"symbol\">Temperature</span></p><p><span class=\"value\"><span id=\"temperature_Fahrenheit\">' + str(temperature_Fahrenheit)+ '</span> &deg;F</span></p> </div> </div> </div> </body> </html>'
        uart0.write(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)
        res="" #reset buffer
        print ('Waiting For connection...')

Create Web page (HTML+CSS)

To build the web page, we will add HTML code and for styling we will add CSS script.

Now let’s go through each line of HTML code to understand how it builds the web page. 

In this HTML document, we use cards, paragraphs, headings, icons and title tags to create a web page. This web page displays temperature readings of DS18B20 sensor in both Celsius and Fahrenheit.

HTML is a hypertext markup language which is used to build web pages. All web browsers understand this language and can read web pages which are based on HTML language. 

In HTML, we place all the content of a web page between <html> and </html> tags. The <html> tag shows the beginning of a web page and the  </html> indicates the end of a web page. 

HTML code mainly includes two parts such as head and body. The head part contains CSS, scripts, meta tags, links of external resources and styling codes. It is placed between <head> and </head> tags. 

'<html> <head> <meta http-equiv=\"refresh\" content=\"10\"> <title>Raspberry Pi Pico Web Server</title> <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;} .top_nav { overflow: hidden; background-color: #da0a0a; color: white; font-size: 1rem; } .content { padding: 30px; } .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); } .cards { max-width: 800px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } .value { font-size: 3rem; } .symbol { font-size: 2rem; } </style> </head>
Title, Icon and HTTP header

We will start with the title of the web page. The <title> tag will indicate the beginning of the title and the </title> tag will indicate the ending. In between these tags, we will specify “Raspberry Pi Pico Web Server” which will be displayed in the browser’s title bar.

<title>Raspberry Pi Pico Web Server</title> 

This meta-tag http-equiv provides attributes to HTTP header. The http-equiv attribute takes many values or information to simulate header response. In this example, we use the http-equiv attribute to refresh the content of the web page after every specified time interval. Users aren’t required to refresh the web page to get updated sensor values. This line forces the HTML page to refresh itself after every 10 seconds. Moreover, this meta tag will make sure our web server is available for all browsers e.g., smartphones, laptops, computers etc.

<meta http-equiv=\"refresh\" content=\"10\">
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> 

In our web page, we will also display an icon of thermometer. This link tag loads the icons used in the webpage from fontawesome.

<link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.7.2/css/all.css\" integrity=\"sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr\" crossorigin=\"anonymous\">
Styling Web Page with CSS

CSS is used to give styles to a web page. To add CSS files in head tags, we use <style></style> tags. This CSS code styles the cards and web page by specifying the colours, font, font size etc.

This CSS code sets text alignment, padding, margin and width of body tags of HTML document along with the font size, colour of the cards etc. We use cards to display Temperature value readings from DS18B20 in centigrade and Fahrenheit units. In HTML, a card is a bordered box that has features such as padding. Content. Header, footers, and colors, etc.

<style> html {font-family: Arial; display: inline-block; text-align: center;} p { font-size: 1.2rem;} body { margin: 0;} .top_nav { overflow: hidden; background-color: #da0a0a; color: white; font-size: 1rem; } .content { padding: 30px; } .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); } .cards { max-width: 800px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } .value { font-size: 3rem; } .symbol { font-size: 2rem; } </style>
HTML Web Page Body

The second most important part of an HTML document is the body which goes inside the tags <body> and </body>. The body part includes the main content of the web page such as headings, images, buttons, icons, tables, charts, etc. For example, in this Raspberry Pi Pico DS18B20 MicroPython based web server, the body part includes heading and two cards to display the temperature readings. 

<body> <div class=\"top_nav\"> <h1>Raspberry Pi Pico DS18B20 Web Server</h1> </div> <div class=\"content\"> <div class=\"cards\"> <div class=\"card\"> <p><i class=\"fas fa-thermometer-half fa-3x\" style=\"color:#da0a0a;\"></i><span class=\"symbol\">Temperature</span></p><p><span class=\"value\"><span id=\"temperature_Celsius\">' + str(temperature_Celsius) + '</span> &deg;C</span></p> </div> <div class=\"card\"> <p><i class=\"fas fa-thermometer-half fa-3x\" style=\"color:#da0a0a;\"></i> <span class=\"symbol\">Temperature</span></p><p><span class=\"value\"><span id=\"temperature_Fahrenheit\">' + str(temperature_Fahrenheit)+ '</span> &deg;F</span></p> </div> </div> </div> </body> </html>'

We will include the heading of our webpage inside the <h1></h1> tags and it will be “Raspberry Pi Pico DS18B20 Web Server”.

<h1>Raspberry Pi Pico DS18B20 Web Server</h1>

Next, we will include the following lines of code to display texts and cards for temperature, readings in both Celsius and Fahrenheit along with the icons.

</div> <div class=\"content\"> <div class=\"cards\"> <div class=\"card\"> <p><i class=\"fas fa-thermometer-half fa-3x\" style=\"color:#da0a0a;\"></i><span class=\"symbol\">Temperature</span></p><p><span class=\"value\"><span id=\"temperature_Celsius\">' + str(temperature_Celsius) + '</span> &deg;C</span></p> </div> <div class=\"card\"> <p><i class=\"fas fa-thermometer-half fa-3x\" style=\"color:#da0a0a;\"></i> <span class=\"symbol\">Temperature</span></p><p><span class=\"value\"><span id=\"temperature_Fahrenheit\">' + str(temperature_Fahrenheit)+ '</span> &deg;F</span></p> </div> </div> </div>

Demonstration

After you have copied the following code onto a new file, click the ‘Save’ icon to save your program code on your PC.

After you have saved the code press the Run button to upload the code to your board. Before uploading code make sure the correct board is selected.

In the shell terminal of your IDE, you will be able to view the IP address after a successful connection gets established:

Raspberry Pi Pico DS18B20 Web Server shell terminal IP address

Now, open your web browser either on your laptop or mobile and type the IP address which we have found in the last step. As soon as you type the IP address on your web browser and hit enter, the Raspberry Pi Pico web server will receive an HTTP request.

Raspberry Pi Pico DS18B20 Web Server Overview

You will see the web page with the latest temperature values in your web browser:

Raspberry Pi Pico DS18B20 Web Server Laptop view

The web page viewed from a cell phone will look like this:

Raspberry Pi Pico DS18B20 Web Server Mobile view

Video Demo:

You may also like to read other Web Server projects with Raspberry Pi Pico:

We have other guides with popular sensors:

Related DS18B20 tutorials and projects:

Leave a Comment