Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

TM1637 display with Raspberry Pi Pico W — internet clock in MicroPython

Turn a TM1637 4-digit display and a Pico W into an IST clock set from internet time (NTP) in MicroPython, with a tiny driver and a blinking colon.

MicroPythonVerified — micropython (mpy-cross v1.29.0), 3 Nov 2026Compile-only

Community draft — report a mistake

A TM1637 module is four 7-segment digits with a colon — the classic clock display. A Pico W can drive it and fetch the correct time from the internet, so the clock never needs setting.

Wire CLK to GP16 and DIO to GP17, power it from 3V3, and add your Wi-Fi name. The display shows the time in IST with a blinking colon.

Wiring

Raspberry Pi Pico WOnly the pins used are shown · 3.3 V logicTM1637 4-digit 7-segment displayGND3V3GP17GP16CLKDIOVCCGND
Every connection
Raspberry Pi Pico WTM1637 4-digit 7-segment displayWireNotes
GP16CLKyellowGP16 is physical pin 21.
GP17DIOgreenGP17 is physical pin 22.
3V3VCCred3V3 is pin 36. The module’s pull-ups then hold CLK/DIO at 3.3 V.
GNDGNDblack

3.3–5 V part, 3.3 V board. Works from 3.3 V or 5 V — power it from your board’s logic voltage so CLK/DIO stay in range. At 3.3 V the digits are a little dimmer.

Board quirks

  • The TM1637 is not I2C even though it has CLK and DIO — any two GPIOs work. The 45-line driver is inside main.py.
  • DIO is set up as Pin.OPEN_DRAIN, so the TM1637 can pull it low for its acknowledge bit without fighting the Pico.
  • With Wi-Fi set, the Pico W fetches time from in.pool.ntp.org, adds 5:30 for IST and re-syncs every 6 hours. Without Wi-Fi it shows a minutes:seconds stopwatch.
  • The Pico loses the time at a power cut and re-syncs on boot. For a clock that must work without Wi-Fi, add a DS3231 (see that guide).

Code

main.pyMicroPython
# TM1637 4-digit display as an internet-synced IST clock on a Raspberry Pi Pico W (MicroPython).
# Wiring: VCC -> 3V3 (pin 36), GND -> GND, CLK -> GP16 (pin 21), DIO -> GP17 (pin 22).
# Without Wi-Fi it shows minutes:seconds since power-up instead.
# The small TM1637 driver below is written for FlashReady (MIT) from the TM1637 datasheet.
from machine import Pin
import network
import time
import json

WIFI_SSID = "your-wifi"      # optional
WIFI_PASS = "your-password"
UTC_OFFSET_S = 5 * 3600 + 30 * 60  # IST

# The FlashReady flasher saves your settings to /settings.json; they win over the lines above.
try:
    with open("/settings.json") as f:
        _s = json.load(f)
except (OSError, ValueError):
    _s = {}
WIFI_SSID = _s.get("wifi_ssid") or WIFI_SSID
WIFI_PASS = _s.get("wifi_pass") or WIFI_PASS

DIGITS = (0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F)
DASH = 0x40


class TM1637:
    def __init__(self, clk, dio, brightness=4):
        self.clk = Pin(clk, Pin.OUT, value=1)
        # Open-drain: writing 1 releases DIO (the module's pull-up lifts it), so the TM1637 can
        # pull it low for its ACK without fighting a pin that is driving HIGH.
        self.dio = Pin(dio, Pin.OPEN_DRAIN, value=1)
        self.brightness = brightness & 7

    def _start(self):
        self.dio(0)
        time.sleep_us(5)
        self.clk(0)

    def _stop(self):
        self.clk(0)
        self.dio(0)
        time.sleep_us(5)
        self.clk(1)
        time.sleep_us(5)
        self.dio(1)

    def _byte(self, b):
        for i in range(8):  # LSB first
            self.clk(0)
            self.dio((b >> i) & 1)
            time.sleep_us(5)
            self.clk(1)
            time.sleep_us(5)
        self.clk(0)  # ninth clock for the ACK (not checked)
        self.dio(1)
        time.sleep_us(5)
        self.clk(1)
        time.sleep_us(5)
        self.clk(0)

    def show(self, segs):
        self._start()
        self._byte(0x40)  # data command: write, auto-increment address
        self._stop()
        self._start()
        self._byte(0xC0)  # start at digit 0
        for s in segs:
            self._byte(s)
        self._stop()
        self._start()
        self._byte(0x88 | self.brightness)  # display on + brightness 0-7
        self._stop()

    def numbers(self, left, right, colon=True):
        segs = [DIGITS[left // 10], DIGITS[left % 10], DIGITS[right // 10], DIGITS[right % 10]]
        if colon:
            segs[1] |= 0x80  # the colon is wired to bit 7 of the second digit
        self.show(segs)


disp = TM1637(16, 17)
disp.show([DASH] * 4)


def ntp_sync():
    if WIFI_SSID == "your-wifi":
        print("No Wi-Fi name set - showing a stopwatch instead of the time.")
        return False
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(WIFI_SSID, WIFI_PASS)
    for _ in range(30):
        if wlan.isconnected():
            break
        time.sleep(0.5)
    if not wlan.isconnected():
        print("Wi-Fi failed - showing a stopwatch instead.")
        return False
    try:
        import ntptime
        ntptime.host = "in.pool.ntp.org"
        ntptime.settime()  # Pico clock = UTC
        print("Time set from NTP")
        return True
    except Exception as e:
        print("NTP failed:", e)
        return False


synced = ntp_sync()
start = time.time()
last_sync = time.time()
while True:
    if synced:
        _, _, _, h, m, s, _, _ = time.localtime(time.time() + UTC_OFFSET_S)
        disp.numbers(h, m, colon=s % 2 == 0)  # blinking colon
        if s == 0:
            print("{:02d}:{:02d} IST".format(h, m))
        if time.time() - last_sync > 6 * 3600:  # re-sync every 6 hours
            last_sync = time.time()
            ntp_sync()
    else:
        up = time.time() - start
        disp.numbers(up // 60 % 100, up % 60)
    time.sleep_ms(500)

Expected serial output

REPL / Thonny shellOutput
Time set from NTP
18:43 IST
18:44 IST

Common errors

Display stays dark

CLK and DIO swapped, or brightness 0. Check GP16 → CLK and GP17 → DIO.

Shows - - - - forever

The display works but the code stopped before the loop — look at the Thonny shell for a Wi-Fi or NTP error.

Time is 5:30 off

You changed UTC_OFFSET_S. NTP gives UTC; IST is 19800 seconds ahead.

↑↓ to move · Enter to open