Raspberry Pi Pico 2 W Project

This page documents hands-on Raspberry Pi Pico 2 W projects using the Freenove Ultimate Starter kit. Python programs interact with LEDs, motors, buttons, and sensors.

Raspberry Pi Pico 2 W
Raspberry Pi Pico 2 W

Raspberry Pi Pico 2 W Specifications

Microcontroller Raspberry Pi RP2350 (dual-core Arm Cortex-M33 + dual-core RISC-V Hazard3)
Clock Speed Up to 150 MHz
SRAM 520 KB on-chip
Flash Memory 4 MB QSPI flash
Wireless 2.4 GHz Wi-Fi (802.11n)
Bluetooth 5.2
GPIO Pins 26 multi-function GPIO
Interfaces 2 Γ— UART
2 Γ— SPI
2 Γ— IΒ²C
USB 1.1 (device)
16 Γ— PWM channels
3 Γ— 12-bit ADC
Operating Voltage 1.8V – 5.5V input (3.3V logic)
Dimensions 51 mm Γ— 21 mm
Release Year 2024

  • Raspberry Pi Pico 2 W (external link)
  • Freenove Ultimate Starter Kit
    Freenove Ultimate Starter Kit with Raspberry Pi Pico 2 W

  • Freenove Ultimate Starter Kit (Freenove external link)
  • Freenove Ultimate Starter Kit (Amazon external link)
  • Note: ChatGPT was used to assist with code development and Raspberry Pi setup.

    Kit Start
    Project start, clean breadboard, ready to burn firmware on Raspberry Pi Pico 2 W

    Burn Micropython Firmware
    Burning Micropython Firmware

    The Raspberry Pi Pico supports both Python (MicroPython) and C/C++ development. Firmware can be flashed to enable either environment. The board includes 4MB of onboard Flash memory for firmware and user programs.

    I have more experience in C/C++, but decided to use Python as a learning experience. It’s faster to prototype with and easier to iterate on.

    Besides… all the cool kids are using Python!

    With the 4MB Flash memory, there is plenty of space for embedded projects:

    • MicroPython firmware: ~600–800 KB
    • Filesystem available for user code: ~3 MB
    • Simple Python file: typically 1–5 KB
    • Medium Python project: typically 10–50 KB
    • Enough room to store multiple projects and reusable helper libraries
    Pico Pins
    Raspberry Pi Pico 2 W Pins

    Thonny Python SDK
    Thonny Python IDE

    Thonny is a lightweight Python IDE with built-in support for MicroPython devices such as the Raspberry Pi Pico.

    When you click Run, Thonny transfers the script to the Pico over USB and executes it directly in RAM for fast testing and iteration.

    If the program is saved as main.py on the Pico’s flash filesystem, it will automatically execute on power-up.

    Flowing Lights

    Video showing the Raspberry Pi Pico 2W controlling an LED bar to display flowing lights using Python.

    View Python source: main.py
    
    #######
    #
    # Project: PWM (Pulse Width Modulation) LED Chase Effect
    # Based on open-source PWM examples from the Raspberry Pi Foundation
    # and MicroPython community.
    #
    # Adapted and documented by Denis Legault
    # Date:    2026-02-10
    # Python File: main.py
    # Hardware: Raspberry Pi Pico 2 W,
    #           10 LEDs connected via PWM-capable pins
    #
    # Description:
    # Demonstrates a PWM-based LED chase effect using a custom myPWM class.
    # The LEDs gradually fade in and out using different duty cycle values,
    # creating a smooth flowing light pattern from left to right and then
    # right to left.
    #
    # PWM duty values are cycled through a predefined list to control
    # brightness levels. The effect runs continuously until interrupted.
    #
    # GPIO Pin Connections:
    # PWM Pins -> GPIO16, 17, 18, 19, 20, 21, 22, 26, 27, 28
    #
    # Notes:
    # - Requires pwm.py containing the myPWM class.
    # - Uses MicroPython PWM (16-bit duty cycle: 0–65535).
    # - Saved as main.py to auto-run on power-up.
    #
    #######
    
    from machine import Pin, PWM
    from pwm import myPWM
    import time
    
    # Initialize PWM controller with 10 GPIO pins
    mypwm = myPWM(16, 17, 18, 19, 20, 21, 22, 26, 27, 28)
    
    # Logical PWM channel numbers (0–9)
    chns = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    # Duty cycle values used to create a fade-in / fade-out effect
    # 0 = OFF, 65535 = FULL brightness
    dutys = [
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        65535, 32768, 16384, 8192, 4096,
        2048, 1024, 512, 256, 128,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    ]
    
    # Delay between animation steps (milliseconds)
    delayTimes = 50
    
    try:
        while True:
            # Forward LED chase
            for i in range(0, 20):
                for j in range(0, 10):
                    # Set PWM duty for each channel
                    mypwm.ledcWrite(chns[j], dutys[i + j])
                time.sleep_ms(delayTimes)
    
            # Reverse LED chase
            for i in range(0, 20):
                for j in range(0, 10):
                    # Reverse channel order for opposite direction
                    mypwm.ledcWrite(chns[9 - j], dutys[i + j])
                time.sleep_ms(delayTimes)
    
    except:
        # Cleanly disable all PWM outputs on exit
        mypwm.deinit()
      
    View Python source: pwm.py
    
    #######
    #
    # Module: PWM Helper Class (myPWM)
    # Based on open-source PWM (Pulse Width Modulation) examples from the 
    # Raspberry Pi Foundation and MicroPython community.
    #
    # Adapted and documented by Denis Legault
    # Date:    2026-02-10
    # Python File: pwm.py
    # Hardware: Raspberry Pi Pico 2 W
    #
    # Description:
    # Provides a simple wrapper class around MicroPython's PWM functionality
    # to control up to 10 PWM output channels using logical channel numbers
    # (0–9) instead of managing individual PWM objects directly.
    #
    # Each PWM channel is initialized with the same frequency and can be
    # updated independently using a 16-bit duty cycle value (0–65535).
    #
    # This module is intended to be imported by other programs (e.g. main.py)
    # and is commonly used for LED dimming effects, motor control, or
    # multi-channel PWM animations.
    #
    # GPIO Pin Connections (default):
    # PWM Channel 0  -> GPIO16
    # PWM Channel 1  -> GPIO17
    # PWM Channel 2  -> GPIO18
    # PWM Channel 3  -> GPIO19
    # PWM Channel 4  -> GPIO20
    # PWM Channel 5  -> GPIO21
    # PWM Channel 6  -> GPIO22
    # PWM Channel 7  -> GPIO26
    # PWM Channel 8  -> GPIO27
    # PWM Channel 9  -> GPIO28
    #
    # Notes:
    # - Uses MicroPython PWM with 16-bit duty resolution.
    # - Frequency defaults to 10 kHz but can be changed during initialization.
    # - Designed to simplify multi-channel PWM usage in embedded projects.
    #
    #######
    
    from machine import Pin, PWM
    
    
    class myPWM:
        """
        Wrapper class for managing multiple PWM outputs using logical channels.
        """
    
        def __init__(
            self,
            pwm0: int = 16,
            pwm1: int = 17,
            pwm2: int = 18,
            pwm3: int = 19,
            pwm4: int = 20,
            pwm5: int = 21,
            pwm6: int = 22,
            pwm7: int = 26,
            pwm8: int = 27,
            pwm9: int = 28,
            freq_num: int = 10000
        ):
            """
            Initialize up to 10 PWM channels with a common frequency.
    
            :param pwm0–pwm9: GPIO pin numbers for each PWM channel
            :param freq_num:  PWM frequency in Hz (default: 10 kHz)
            """
    
            # Create PWM objects for each channel and set frequency
            self._pwm0 = PWM(Pin(pwm0))
            self._pwm0.freq(freq_num)
    
            self._pwm1 = PWM(Pin(pwm1))
            self._pwm1.freq(freq_num)
    
            self._pwm2 = PWM(Pin(pwm2))
            self._pwm2.freq(freq_num)
    
            self._pwm3 = PWM(Pin(pwm3))
            self._pwm3.freq(freq_num)
    
            self._pwm4 = PWM(Pin(pwm4))
            self._pwm4.freq(freq_num)
    
            self._pwm5 = PWM(Pin(pwm5))
            self._pwm5.freq(freq_num)
    
            self._pwm6 = PWM(Pin(pwm6))
            self._pwm6.freq(freq_num)
    
            self._pwm7 = PWM(Pin(pwm7))
            self._pwm7.freq(freq_num)
    
            self._pwm8 = PWM(Pin(pwm8))
            self._pwm8.freq(freq_num)
    
            self._pwm9 = PWM(Pin(pwm9))
            self._pwm9.freq(freq_num)
    
        def ledcWrite(self, chn, value):
            """
            Set the PWM duty cycle for a given channel.
    
            :param chn:   Logical PWM channel number (0–9)
            :param value: Duty cycle (0–65535)
            """
    
            if chn == 0:
                self._pwm0.duty_u16(value)
            elif chn == 1:
                self._pwm1.duty_u16(value)
            elif chn == 2:
                self._pwm2.duty_u16(value)
            elif chn == 3:
                self._pwm3.duty_u16(value)
            elif chn == 4:
                self._pwm4.duty_u16(value)
            elif chn == 5:
                self._pwm5.duty_u16(value)
            elif chn == 6:
                self._pwm6.duty_u16(value)
            elif chn == 7:
                self._pwm7.duty_u16(value)
            elif chn == 8:
                self._pwm8.duty_u16(value)
            elif chn == 9:
                self._pwm9.duty_u16(value)
    
        def deinit(self):
            """
            Disable all PWM channels and release hardware resources.
            """
    
            self._pwm0.deinit()
            self._pwm1.deinit()
            self._pwm2.deinit()
            self._pwm3.deinit()
            self._pwm4.deinit()
            self._pwm5.deinit()
            self._pwm6.deinit()
            self._pwm7.deinit()
            self._pwm8.deinit()
            self._pwm9.deinit()
      
    RGB LED

    Video showing the Raspberry Pi Pico 2W controlling of a NeoPixel (WS2812) LED strip using a custom myNeopixel class. The program cycles through a predefined list of colors (red, green, blue, white, and off), displaying each color across all LEDs for 0.5 seconds.

    View Python source: main.py
    
    #######
    #
    # Project: NeoPixel Color Cycle
    # Based on open-source WS2812 PIO examples from the Raspberry Pi Foundation
    # and MicroPython community.
    #
    # Adapted and documented by Denis Legault
    # Date:    2026-02-10
    # Python File: main.py
    # Hardware: Raspberry Pi Pico 2 W,
    #           8x WS2812 / NeoPixel LEDs connected to GPIO16
    #
    # Description:
    # Demonstrates basic control of a NeoPixel (WS2812) LED strip using a
    # custom myNeopixel class. The program cycles through a predefined list
    # of colors (red, green, blue, white, and off), displaying each color
    # across all LEDs for 0.5 seconds.
    #
    # The brightness level is set globally before entering the loop.
    # The effect runs continuously until interrupted.
    #
    # GPIO Pin Connections:
    # NeoPixel Data In -> GPIO16
    # 5V               -> External 5V supply (recommended)
    # GND              -> Common ground with Pico
    #
    # Notes:
    # - Requires neopixel.py containing the myNeopixel class.
    # - NeoPixels use RGB color values (0–255 per channel).
    # - brightness() scales overall intensity (0–255).
    # - Saved as main.py to auto-run on power-up.
    #
    #######
    
    import time
    from machine import Pin
    from neopixel import myNeopixel
    
    # Number of LEDs in the strip
    NUM_LEDS = 8
    
    # Initialize NeoPixel object
    # Parameters: (number_of_leds, GPIO_pin)
    np = myNeopixel(NUM_LEDS, 16)
    
    # Define RGB color tuples
    red = (255, 0, 0)
    green = (0, 255, 0)
    blue = (0, 0, 255)
    white = (255, 255, 255)
    close = (0, 0, 0)  # LEDs off
    
    # List of colors to cycle through
    COLORS = [red, green, blue, white, close]
    
    # Set global brightness (0–255)
    np.brightness(10)  # Low brightness to reduce power draw
    
    while True:
        # Loop through each predefined color
        for color in COLORS:
    
            # Set all LEDs to the current color
            np.fill(color[0], color[1], color[2])
    
            # Update the strip to display changes
            np.show()
    
            # Wait before changing to the next color
            time.sleep(0.5)
      
    View Python source: neopyxel.py
    
    #######
    #
    # Module: NeoPixel (WS2812) Driver using PIO
    # Based on open-source WS2812 PIO examples from the Raspberry Pi Foundation
    # and MicroPython community.
    #
    # Adapted and documented by Denis Legault
    # Date: 2026-02-10
    # Python File: neopixel.py
    # Hardware: Raspberry Pi Pico / Pico 2 W,
    #           WS2812 / NeoPixel LED strip
    #
    # Description:
    # Provides a custom NeoPixel driver using the RP2040/RP2350 PIO (Programmable
    # I/O) subsystem for precise timing control required by WS2812 LEDs.
    #
    # The module defines:
    # - A PIO assembly routine (ws2812) to generate the required waveform
    # - A myNeopixel class to manage LED buffers, brightness control,
    #   gradients, rotation, and display updates
    #
    # NeoPixels require strict timing (800kHz data stream). This implementation
    # offloads signal generation to PIO hardware, ensuring accurate and stable
    # communication without blocking the CPU.
    #
    # Notes:
    # - Uses 24-bit GRB color format (8 bits per channel).
    # - Brightness is software-scaled before transmission.
    # - Requires 5V power supply for most LED strips.
    # - Ground must be shared between Pico and LED strip.
    #
    #######
    
    import array, time
    from machine import Pin
    import rp2
    
    
    # ------------------------------------------------------------
    # PIO Assembly Program for WS2812
    # ------------------------------------------------------------
    # Generates precise timing signals required by NeoPixel LEDs.
    # This runs independently in hardware (PIO state machine).
    # ------------------------------------------------------------
    @rp2.asm_pio(
        sideset_init=rp2.PIO.OUT_LOW,
        out_shiftdir=rp2.PIO.SHIFT_LEFT,
        autopull=True,
        pull_thresh=24
    )
    def ws2812():
        T1 = 2
        T2 = 5
        T3 = 8
    
        wrap_target()
        label("bitloop")
        out(x, 1)              .side(0)    [T3 - 1]
        jmp(not_x, "do_zero")  .side(1)    [T1 - 1]
        jmp("bitloop")         .side(1)    [T2 - 1]
        label("do_zero")
        nop()                  .side(0)    [T2 - 1]
        wrap
    
    
    # ------------------------------------------------------------
    # NeoPixel Class
    # ------------------------------------------------------------
    class myNeopixel:
        def __init__(self, num_leds, pin, delay_ms=1):
            """
            Initialize NeoPixel driver.
    
            :param num_leds: Number of LEDs in the strip
            :param pin: GPIO pin used for data output
            :param delay_ms: Delay after updating LEDs (stability)
            """
    
            # Create pixel buffer (32-bit integers per LED)
            self.pixels = array.array("I", [0 for _ in range(num_leds)])
    
            # Use State Machine 0
            self.state_machine = 0
    
            # Initialize PIO state machine with 8 MHz frequency
            self.sm = rp2.StateMachine(
                self.state_machine,
                ws2812,
                freq=8000000,
                sideset_base=Pin(pin)
            )
    
            # Activate state machine
            self.sm.active(1)
    
            self.num_leds = num_leds
            self.delay_ms = delay_ms
            self.brightnessvalue = 255  # Default full brightness
    
        # --------------------------------------------------------
        # Brightness Control
        # --------------------------------------------------------
        def brightness(self, brightness=None):
            """
            Get or set global brightness (1–255).
            """
            if brightness is None:
                return self.brightnessvalue
            else:
                if brightness < 1:
                    brightness = 1
                if brightness > 255:
                    brightness = 255
    
                self.brightnessvalue = brightness
    
        # --------------------------------------------------------
        # Gradient between two pixels
        # --------------------------------------------------------
        def set_pixel_line_gradient(
            self,
            pixel1,
            pixel2,
            left_r,
            left_g,
            left_b,
            right_r,
            right_g,
            right_b
        ):
            """
            Create a color gradient between two pixel positions.
            """
            if pixel2 - pixel1 == 0:
                return
    
            right_pixel = max(pixel1, pixel2)
            left_pixel = min(pixel1, pixel2)
    
            for i in range(right_pixel - left_pixel + 1):
                fraction = i / (right_pixel - left_pixel)
                red = round((right_r - left_r) * fraction + left_r)
                green = round((right_g - left_g) * fraction + left_g)
                blue = round((right_b - left_b) * fraction + left_b)
    
                self.set_pixel(left_pixel + i, red, green, blue)
    
        # --------------------------------------------------------
        # Set solid color across a pixel range
        # --------------------------------------------------------
        def set_pixel_line(self, pixel1, pixel2, r, g, b):
            for i in range(pixel1, pixel2 + 1):
                self.set_pixel(i, r, g, b)
    
        # --------------------------------------------------------
        # Set individual pixel color
        # --------------------------------------------------------
        def set_pixel(self, pixel_num, r, g, b):
            """
            Set a single pixel color with brightness scaling.
            WS2812 uses GRB format internally.
            """
    
            # Apply brightness scaling
            blue = round(b * (self.brightness() / 255))
            red = round(r * (self.brightness() / 255))
            green = round(g * (self.brightness() / 255))
    
            # Store packed GRB value
            self.pixels[pixel_num] = blue | red << 8 | green << 16
    
        # --------------------------------------------------------
        # Rotate pixel buffer left
        # --------------------------------------------------------
        def rotate_left(self, num_of_pixels):
            if num_of_pixels is None:
                num_of_pixels = 1
            self.pixels = self.pixels[num_of_pixels:] + self.pixels[:num_of_pixels]
    
        # --------------------------------------------------------
        # Rotate pixel buffer right
        # --------------------------------------------------------
        def rotate_right(self, num_of_pixels):
            if num_of_pixels is None:
                num_of_pixels = 1
            num_of_pixels = -1 * num_of_pixels
            self.pixels = self.pixels[num_of_pixels:] + self.pixels[:num_of_pixels]
    
        # --------------------------------------------------------
        # Send pixel data to LED strip
        # --------------------------------------------------------
        def show(self):
            """
            Transmit pixel buffer to LEDs via PIO.
            """
            for i in range(self.num_leds):
                self.sm.put(self.pixels[i], 8)
    
            # Small delay ensures data latch timing
            time.sleep_ms(self.delay_ms)
    
        # --------------------------------------------------------
        # Fill entire strip with one color
        # --------------------------------------------------------
        def fill(self, r, g, b):
            """
            Set all LEDs to a single color.
            """
            for i in range(self.num_leds):
                self.set_pixel(i, r, g, b)
    
            time.sleep_ms(self.delay_ms)
      
    Rainbow Lights

    Video showing the Raspberry Pi Pico 2W controlling a moving rainbow animation across a NeoPixel strip. A colour wheel function generates smooth RGB transitions, and each LED is offset slightly to create a flowing rainbow effect.

    View Python source: main.py
    
    #######
    #
    # Project: NeoPixel Rainbow Animation
    # Based on open-source WS2812 PIO examples from the Raspberry Pi Foundation
    # and MicroPython community.
    #
    # Adapted and documented by Denis Legault
    # Date:    2026-02-12
    # Python File: main.py
    # Hardware: Raspberry Pi Pico 2 W,
    #           8x WS2812 / NeoPixel LEDs connected to GPIO16
    #
    # Description:
    # Demonstrates a moving rainbow animation across a NeoPixel strip.
    # A color wheel function generates smooth RGB transitions, and each
    # LED is offset slightly to create a flowing rainbow effect.
    #
    # The animation continuously cycles through 0–254 color positions,
    # producing a seamless moving rainbow pattern.
    #
    # GPIO Pin Connections:
    # NeoPixel Data In -> GPIO16
    # 5V               -> External 5V supply (recommended)
    # GND              -> Common ground with Pico
    #
    # Notes:
    # - Requires neopixel.py containing the myNeopixel class.
    # - Uses 24-bit RGB color values (0–255 per channel).
    # - Brightness is limited to reduce current draw.
    # - Saved as main.py to auto-run on power-up.
    #
    #######
    
    from machine import Pin
    from neopixel import myNeopixel
    import time
    
    # Number of LEDs in the strip
    NUM_LEDS = 8
    
    # Initialize NeoPixel object on GPIO16
    np = myNeopixel(NUM_LEDS, 16)
    
    # Global RGB values updated by wheel()
    red = 0
    green = 0
    blue = 0
    
    
    # ------------------------------------------------------------
    # Color Wheel Function
    # ------------------------------------------------------------
    # Generates smooth RGB transitions across a 0–254 range.
    # This creates a full rainbow spectrum.
    # ------------------------------------------------------------
    def wheel(pos):
        global red, green, blue
    
        WheelPos = pos % 255  # Ensure value wraps around 0–254
    
        # Red -> Green transition
        if WheelPos < 85:
            red = (255 - WheelPos * 3)
            green = (WheelPos * 3)
            blue = 0
    
        # Green -> Blue transition
        elif WheelPos < 170:
            WheelPos -= 85
            red = 0
            green = (255 - WheelPos * 3)
            blue = (WheelPos * 3)
    
        # Blue -> Red transition
        else:
            WheelPos -= 170
            red = (WheelPos * 3)
            green = 0
            blue = (255 - WheelPos * 3)
    
    
    # Set global brightness (0–255)
    np.brightness(20)  # Lower brightness reduces power consumption
    
    while True:
        # Cycle through full color wheel range
        for i in range(0, 255):
    
            # Update each LED with an offset to create moving effect
            for j in range(0, NUM_LEDS):
                wheel(i + j * 255 // NUM_LEDS)
                np.set_pixel(j, red, green, blue)
    
            # Send updated pixel data to strip
            np.show()
    
            # Short delay controls animation speed
            time.sleep_ms(1)
      

    See previous video above for neopixel.py code

    4WD Car LED Pico
    4WD Smart Car LED controlled by Pico

    While working on the Freenove 4WD Smart Car, I ran into an issue controlling the onboard WS2812 (NeoPixel) LEDs using a Raspberry Pi 5. The LEDs would partially light or behave unpredictably, suggesting a timing-related problem rather than a hardware fault.

    After learning more about how WS2812 LEDs work β€” specifically their strict 800 kHz pulse-width timing requirements β€” I experimented with using a Raspberry Pi Pico 2 W instead of the Raspberry Pi 5.

    I disconnected the Pi 5 from the LED data line (GPIO10 on PCB V2.0) and connected the Pico directly to the WS2812 data input. Using the Pico’s PIO (Programmable I/O) hardware, I was able to generate precise timing signals and successfully control all 8 LEDs, including smooth animations such as rainbow effects and a Knight Rider scanner pattern.

    This experiment proved that:

    • The LED hardware and wiring were functioning correctly.
    • The issue was not power-related.
    • The problem was due to timing sensitivity when generating the WS2812 signal from Linux on the Raspberry Pi 5.

    By offloading LED control to the Pico β€” a microcontroller designed for deterministic, real-time signal generation β€” the LEDs operated flawlessly.

    Use a microcontroller for precise timing tasks, and a Linux-based system for high-level control and networking.

    The Pico could be used as a dedicated LED coprocessor for the 4WD car.

    7 Segment Display and 74HC595

    Hexadecimal counter (0–F) displayed on a 7-segment LED using a 74HC595 shift register and Raspberry Pi Pico 2 W. The decimal point flashes for each digit. Demonstrates bit-level control and serial-to-parallel output expansion.

    View Python source: main.py
    
    #######
    #
    # Project: 7-Segment Counter with Flashing Decimal Point
    # Based on common 74HC595 shift register usage patterns and
    # 7-segment display encoding examples.
    #
    # Adapted and documented by Denis Legault
    # Date:    2026-03-01
    # Python File: main.py
    # Hardware: Raspberry Pi Pico 2 W,
    #           74HC595 Shift Register,
    #           1x Common Anode 7-Segment Display
    #
    # Description:
    # Displays hexadecimal digits (0–F) on a 7-segment display
    # using a 74HC595 shift register. The decimal point (DP)
    # flashes ON and OFF for each digit.
    #
    # Display Type:
    #   Common Anode (Active LOW)
    #   - 0 = Segment ON
    #   - 1 = Segment OFF
    #
    # Bit Layout (based on wiring configuration):
    #   Bit 7 β†’ DP (Decimal Point)
    #   Bit 6 β†’ Segment A
    #   Bit 5 β†’ Segment B
    #   Bit 4 β†’ Segment C
    #   Bit 3 β†’ Segment D
    #   Bit 2 β†’ Segment E
    #   Bit 1 β†’ Segment F
    #   Bit 0 β†’ Segment G
    #
    # Notes:
    # - Uses LSB-first shifting via 74HC595.
    # - Decimal point controlled via bit mask (0x80).
    #
    #######
    
    import time
    from my74HC595 import Chip74HC595
    
    # ------------------------------------------------------------
    # 7-Segment Encoding Table (Hexadecimal 0–F)
    # ------------------------------------------------------------
    SEGMENT_MAP = [
        0xC0,  # 0
        0xF9,  # 1
        0xA4,  # 2
        0xB0,  # 3
        0x99,  # 4
        0x92,  # 5
        0x82,  # 6
        0xF8,  # 7
        0x80,  # 8
        0x90,  # 9
        0x88,  # A
        0x83,  # b
        0xC6,  # C
        0xA1,  # d
        0x86,  # E
        0x8E   # F
    ]
    
    
    # ------------------------------------------------------------
    # Decimal Point Helper (Active LOW)
    # ------------------------------------------------------------
    def set_dp(value, on):
        """
        Enable or disable decimal point.
        Common Anode display β†’ active LOW.
        """
        if on:
            return value & ~0x80   # Clear bit 7 β†’ DP ON
        else:
            return value | 0x80    # Set bit 7 β†’ DP OFF
    
    
    # Initialize 74HC595
    chip = Chip74HC595(18, 20, 21)
    
    
    # ------------------------------------------------------------
    # Main Loop
    # ------------------------------------------------------------
    while True:
        for count in range(16):
    
            value = SEGMENT_MAP[count]
    
            # Display digit with DP ON
            chip.shiftOut(0, set_dp(value, True))
            time.sleep(0.5)
    
            # Display digit with DP OFF
            chip.shiftOut(0, set_dp(value, False))
            time.sleep(0.5)
    
      
    View Python source: my74HC595.py
    
    #######
    #
    # Module: 74HC595 Shift Register Driver
    # Based on common 74HC595 shift register usage patterns and
    # MicroPython GPIO examples.
    #
    # Adapted and documented by Denis Legault
    # Date:    2026-03-01
    # Python File: chip74hc595.py
    # Hardware: Raspberry Pi Pico 2 W,
    #           74HC595 8-bit shift register
    #
    # Description:
    # Provides a simple MicroPython driver for controlling a 74HC595
    # serial-in / parallel-out shift register using GPIO pins.
    #
    # The class allows:
    # - Shifting out 8-bit values (MSB-first or LSB-first)
    # - Clearing the register
    # - Enabling and disabling output
    #
    # The 74HC595 is commonly used to expand GPIO output pins,
    # allowing control of multiple LEDs or digital outputs using
    # only three control lines from the Pico.
    #
    # 74HC595 Pin Connections:
    # 14 (DS)   -> Pico GPIO18  (Serial Data)
    # 12 (STCP) -> Pico GPIO20  (Storage Register Clock / Latch)
    # 11 (SHCP) -> Pico GPIO21  (Shift Register Clock)
    # 13 (OE)   -> Pico GPIO19  (Output Enable, active LOW)
    # 16 (VCC)  -> 5V or 3.3V (depending on design)
    # 8  (GND)  -> Ground (shared with Pico)
    #
    # Notes:
    # - Outputs Q0–Q7 update only after latch (STCP) pulse.
    # - OE is active LOW (LOW = outputs enabled).
    # - Multiple 74HC595 chips can be daisy-chained.
    #
    #######
    
    from machine import Pin
    
    
    class Chip74HC595(object):
        def __init__(self, ds: int = 18, stcp: int = 20, shcp: int = 21, oe: int = 19):
            """
            Initialize 74HC595 control pins.
    
            :param ds:   Serial Data input (DS)
            :param stcp: Storage Register Clock (Latch)
            :param shcp: Shift Register Clock
            :param oe:   Output Enable (active LOW)
            """
    
            # Configure control pins as outputs
            self._ds = Pin(ds, Pin.OUT, value=0)      # Data pin
            self._shcp = Pin(shcp, Pin.OUT, value=0)  # Shift clock
            self._stcp = Pin(stcp, Pin.OUT, value=0)  # Latch clock
            self._oe = Pin(oe, Pin.OUT, value=0)      # Output enable
    
            # Enable outputs by default
            self.enable()
    
        # --------------------------------------------------------
        # Shift out 8 bits of data
        # --------------------------------------------------------
        def shiftOut(self, direction, data):
            """
            Shift out one byte (8 bits) to the register.
    
            :param direction: True = MSB first, False = LSB first
            :param data:      8-bit integer value (0–255)
            """
    
            # Prepare clocks
            self._shcp.on()
            self._stcp.on()
    
            # MSB first
            if direction:
                for i in range(8):
                    bit = data << i
                    bit = bit & 0x80
    
                    if bit == 0x80:
                        self._ds.on()
                    else:
                        self._ds.off()
    
                    self._shift_bit()
    
                self._send_data()
    
            # LSB first
            if not direction:
                for i in range(8):
                    bit = data >> i
                    bit = bit & 0x01
    
                    if bit == 0x01:
                        self._ds.on()
                    else:
                        self._ds.off()
    
                    self._shift_bit()
    
                self._send_data()
    
        # --------------------------------------------------------
        # Clear all outputs
        # --------------------------------------------------------
        def clear(self):
            """
            Clear the shift register (all outputs LOW).
            """
            for i in range(8):
                self._ds.off()
                self._shift_bit()
    
            self._send_data()
            self.enable()
    
        # --------------------------------------------------------
        # Pulse shift clock (SHCP)
        # --------------------------------------------------------
        def _shift_bit(self):
            """
            Clock a single bit into the shift register.
            """
            self._shcp.off()
            self._shcp.on()
    
        # --------------------------------------------------------
        # Pulse latch clock (STCP)
        # --------------------------------------------------------
        def _send_data(self):
            """
            Latch shifted data to output pins Q0–Q7.
            """
            self._stcp.off()
            self._stcp.on()
    
        # --------------------------------------------------------
        # Output Enable Control
        # --------------------------------------------------------
        def disable(self):
            """
            Disable outputs (OE HIGH).
            """
            self._oe.on()
    
        def enable(self):
            """
            Enable outputs (OE LOW).
            """
            self._oe.off()
    
      

    To be continued ...