Built-in project · Raspberry Pi Pico W

Pico W WiFi weather and time on an I2C LCD

MicroPython on a Pico W: join the simulated WiFi, fetch the weather and the time with urequests, and show both on an I2C LCD1602 on GP6 and GP7. The whole build, a Raspberry Pi Pico W and 1 more part, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 15 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $10. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Joining WiFi from a Pico W with the network module
  • Fetching data over HTTP with urequests
  • Parsing a JSON reply into values
  • Talking to an I2C LCD on the board's second I2C bus
  • Difficulty: Intermediate
  • Time: About 15 minutes
  • Board: Raspberry Pi Pico W
  • Parts: 3

How it works

A Pico W runs MicroPython. It first shows Connecting on the LCD, then joins the simulated WiFi network Mokxi-Lab. If it cannot join within ten seconds, it raises an error.

Once connected, it loops forever. It asks http://weather.mokxi/ for weather as JSON and http://time.mokxi/ for the time as text. It shows the hour and minute with the temperature on line one, and humidity on line two. Then it waits 10 seconds. The LCD is on I2C pins GP6 and GP7.

Parts list

3 parts, plus the jumper wires. They retail for about $10; on Mokxi they're free.

Kits that can build it

These 2 starter kits have every part in the circuit. Each kit's page lists what else it can build.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Character LCD with I2C backpack pin SDARaspberry Pi Pico W pin GP6
Character LCD with I2C backpack pin SCLRaspberry Pi Pico W pin GP7
Character LCD with I2C backpack pin GNDGround
Character LCD with I2C backpack pin VCC5 V

The code

The MicroPython code exactly as the editor opens it. Change a line there and press Run: the board runs it on the official MicroPython firmware, with nothing to compile.

main.py · Raspberry Pi Pico W
# Real-time weather: join WiFi, fetch the weather and the time, and show
# them on the LCD1602 (I2C1, GP6 and GP7). In Mokxi the weather comes from
# http://weather.mokxi/ and the time from http://time.mokxi/, both on the
# simulated network; the lesson's own program asks OpenWeatherMap and an
# NTP server on the real internet.
from machine import I2C, Pin
from lcd1602 import LCD1602
import network
import time
import urequests

SSID = "Mokxi-Lab"
PASSWORD = "breadboard"
lcd = LCD1602(I2C(1, sda=Pin(6), scl=Pin(7), freq=400000))

def connect(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)
    for _ in range(20):                    # give it ten seconds
        if wlan.isconnected():
            return wlan.ifconfig()[0]
        time.sleep_ms(500)
    raise RuntimeError("could not join " + ssid)

lcd.show("Connecting...")
connect(SSID, PASSWORD)

while True:
    r = urequests.get("http://weather.mokxi/")
    w = r.json()
    r.close()
    r = urequests.get("http://time.mokxi/")
    clock = r.text.strip()
    r.close()
    hhmm = clock[11:16] if len(clock) >= 16 else clock[:16]
    lcd.show("%s %.0fC" % (hhmm, w["temperatureC"]), "Humidity %d%%" % w["humidity"])
    print(hhmm, w["location"], "%.1f C, %d %%" % (w["temperatureC"], w["humidity"]))
    time.sleep(10)
lcd1602.py · Raspberry Pi Pico W
# lcd1602.py: a 16x2 character LCD on a PCF8574 I2C backpack.
# The backpack's P0 is RS, P2 is E, P3 is the backlight and P4 to P7 are
# the display's D4 to D7, so every byte is sent as two 4-bit halves.
import time

class LCD1602:
    def __init__(self, i2c, addr=0x27):
        self.i2c = i2c
        self.addr = addr
        self.light = 0x08
        time.sleep_ms(50)
        for half in (0x30, 0x30, 0x30, 0x20):   # wake up, then 4-bit mode
            self._half(half, 0)
            time.sleep_ms(5)
        self.command(0x28)                      # two lines, 5x8 dots
        self.command(0x0C)                      # display on, no cursor
        self.command(0x06)                      # move right after a character
        self.clear()

    def _send(self, bits):
        self.i2c.writeto(self.addr, bytes([bits | self.light]))

    def _half(self, bits, rs):
        self._send(bits | rs | 0x04)            # E high...
        self._send(bits | rs)                   # ...and low: the LCD reads it

    def _byte(self, value, rs):
        self._half(value & 0xF0, rs)
        self._half((value << 4) & 0xF0, rs)

    def command(self, value):
        self._byte(value, 0)
        if value < 4:
            time.sleep_ms(2)                    # clear and home are slow

    def clear(self):
        self.command(0x01)

    def move_to(self, col, row):
        self.command(0x80 | (col + 0x40 * row))

    def write(self, text):
        for ch in text:
            self._byte(ord(ch), 1)

    def show(self, line1, line2=""):
        self.move_to(0, 0)
        self.write("%-16s" % line1[:16])
        self.move_to(0, 1)
        self.write("%-16s" % line2[:16])

Code walkthrough

The key functions of the sketch, and what each one does.

connect main.py

Turns the WiFi radio on in station mode and starts joining with the name and password. It then checks up to 20 times, half a second apart, for a connection. When joined it returns the board's IP address. If time runs out it raises an error that names the network.

def connect(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)
    for _ in range(20):                    # give it ten seconds
        if wlan.isconnected():
            return wlan.ifconfig()[0]
        time.sleep_ms(500)
    raise RuntimeError("could not join " + ssid)

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The program stops with could not join This project
    Check that SSID and PASSWORD match the simulated network, Mokxi-Lab and breadboard.
  • The LCD stays blank This project
    Check that SDA is on GP6, SCL is on GP7, and the LCD has 5 V and ground.
  • The screen stays blank or shows only a row of blocks. Character LCD with I2C backpack
    The I2C address is usually 0x27 or 0x3F. Run an I2C scanner to find yours. Then turn the contrast trimmer on the backpack slowly until text appears.
  • The backlight is on but there is no text, or text is garbled. Character LCD with I2C backpack
    Wire SDA and SCL to the right pins (A4 and A5 on an Uno), power it from 5 V, and call lcd.init() and the backlight call in setup.
  • Pins are powered from 5 V, or a 5 V signal goes into a GPIO. The RP2040 pins are 3.3 V only and are not 5 V tolerant. Raspberry Pi Pico W
    Power the board from VBUS or VSYS at 5 V, but keep GPIO signals at 3.3 V. Use a level shifter or a divider for 5 V parts.
  • WiFi code fails to connect, or the on-board LED code does nothing. The LED on the Pico W is not on GP25 like on the plain Pico. Raspberry Pi Pico W
    Use the board's LED_BUILTIN name so the library drives it through the WiFi chip. Join a 2.4 GHz network only, with the right SSID and password, and wait for the link before sending.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.