element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
7-Segment Display
  • Challenges & Projects
  • Project14
  • 7-Segment Display
  • More
  • Cancel
7-Segment Display
Blog The VCR Emulator
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Events
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: ntewinkel
  • Date Created: 7 Jun 2022 10:14 PM Date Created
  • Views 2401 views
  • Likes 8 likes
  • Comments 5 comments
  • clock
  • 7segmentdisplaych
  • 7-segment
  • timer
  • raspberry pi pico
  • 7-segment display
  • 7segmentdisplay
  • 7segmentdisplaysch
  • vcr
  • pi pico
Related
Recommended

The VCR Emulator

ntewinkel
ntewinkel
7 Jun 2022

I know you all secretly miss the 80's, with the big oversized electronics that our parents didn't know how to use.
Back then you'd really get something for your money - the cost per pound of electronic equipment was so much better. Today, however, it's a different story... consider the Apple Watch - that little thing can cost you over $10,000 per pound! But I digress...
So I'm here to help bring back some of those memories for you, bringing back some nostalgia. You're welcome Wink

In my previous blog post I built a basic count-down timer using a 4-digit 7-segment clock display and a Pi Pico.

Over the weekend, I added a few small extensions to the code.

Firstly, I wanted it to flash all zeroes when the timer hits zero. Just to make it a bit more obvious that the countdown has completed.
But then I realized that, for a tiny bit of extra coding effort, I can single-handedly improve the quality of life for those of us from the 70s and 80s, and thus I made a minor adjustment to make it emulate a VCR :D

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

All of it was actually a relatively small and easy change - I added a flag to indicate when it's flashing, and I added a vcr_mode flag. Then a bit of extra code that uses the flashing divider colon logic to hide/show all of the digits when the colon is showing or hidden.

The flashing flag gets set when the timer hits zero.

A little bit of tweaking in the function that sets the digits into the display buffer ensures all four digits flash 0 when the time is up, and I added a separate function to set the display buffer to 12:00 for the vcr mode.

So, essentially, after completing the flashing-zeroes detail, the VCR emulator code probably comes down to just few lines of simple code.

One detail I keep bumping into with Python is the idea of global variables! It seems you can access global variables all will nily from anywhere, but if you try to change a global variable inside of a function, it goes and creates a local variable... that leads to some head-scratching-moment type of bugs! You'll see that in most cases I try to define and declare them at the top in a way similar to the Arduino or other compiled languages, and then I explicitly list the ones being used in the functions - mostly to keep myself from getting confused.

The Code:

#
# VCR Emulator
#
#

from machine import Pin, Timer
import time


# For my clock display, the common wires connects to pins that must be set HIGH to enable each position
#  -> Note that this means we have to turn a pin "off" to light the segment LED, and "on" to turn it off
#
# You can test the display by using a small 3.3v button battery (CR2032 seems cheap and plentiful)
# Hopefully the display you have will have a model number you can look up to find the pinout.

# Define the segment GPIO connections
# hook up the segments as per the defined constants below
# Use a current limiting resistor for each segment (you'll need 7!)
# The resistors are also necessary to keep a constant brightness on the display
SEG_A_PIN = 13
SEG_B_PIN = 19
SEG_C_PIN = 17
SEG_D_PIN = 16
SEG_E_PIN = 12  # oops, I put the display too close to the Pi Pico, so pin 15 is covered.
SEG_F_PIN = 14
SEG_G_PIN = 18

# The clock display has 4 digit positions
# I'm calling them positions, as it could be hr:mm or mm:ss
POSITION_1 = 10
POSITION_2 = 11
POSITION_3 = 20
POSITION_4 = 21

DIVIDER_COLON = 22

# Python allows us to define global variables anywhere all willy-nilly,
#   but for clarity lets define them here at the top like good little programmers
#   The type is here just for clarity too - Python allows us to change it at any time
DIGITS :[[Pin]] = []
BLANK_DIGIT : [Pin] = []
POSITIONS : [Pin] = []

# time.time() returns float number of seconds since epoch
MINUTE : float = 60
HOUR = MINUTE * 60
TOTAL_TIME = 1 * MINUTE

time_left = TOTAL_TIME
start_time : float = 0 # in seconds since epoch
colon_visible = False
display_buffer : [[Pin]] = [BLANK_DIGIT, BLANK_DIGIT, BLANK_DIGIT, BLANK_DIGIT]  # this will hold the 4 digits currently being displayed - the display needs continuous refreshing in order to work.

flashing = False
vcr_mode = False  # set to True for some nostalgic effects


def setup():
    # Define each segment
    SEG_A = Pin(SEG_A_PIN, Pin.OUT)
    SEG_B = Pin(SEG_B_PIN, Pin.OUT)
    SEG_C = Pin(SEG_C_PIN, Pin.OUT)
    SEG_D = Pin(SEG_D_PIN, Pin.OUT)
    SEG_E = Pin(SEG_E_PIN, Pin.OUT)
    SEG_F = Pin(SEG_F_PIN, Pin.OUT)
    SEG_G = Pin(SEG_G_PIN, Pin.OUT)
    
    # Define which segments make up each digit
    DIGIT_0 = [SEG_A, SEG_B, SEG_C, SEG_D, SEG_E, SEG_F       ]
    DIGIT_1 = [       SEG_B, SEG_C                            ]
    DIGIT_2 = [SEG_A, SEG_B,        SEG_D, SEG_E,        SEG_G]
    DIGIT_3 = [SEG_A, SEG_B, SEG_C, SEG_D,               SEG_G]
    DIGIT_4 = [       SEG_B, SEG_C,               SEG_F, SEG_G]
    DIGIT_5 = [SEG_A,        SEG_C, SEG_D,        SEG_F, SEG_G]
    DIGIT_6 = [SEG_A,        SEG_C, SEG_D, SEG_E, SEG_F, SEG_G]
    DIGIT_7 = [SEG_A, SEG_B, SEG_C                            ]
    DIGIT_8 = [SEG_A, SEG_B, SEG_C, SEG_D, SEG_E, SEG_F, SEG_G]
    DIGIT_9 = [SEG_A, SEG_B, SEG_C, SEG_D,        SEG_F, SEG_G]
    
    # Note that we are not limited to decimal digits. We could continue to add A through F for hexadecimal
    
    POS_1 = Pin(POSITION_1, Pin.OUT)
    POS_2 = Pin(POSITION_2, Pin.OUT)
    POS_3 = Pin(POSITION_3, Pin.OUT)
    POS_4 = Pin(POSITION_4, Pin.OUT)

    global divider_colon
    divider_colon = Pin(DIVIDER_COLON, Pin.OUT)
    
    global DIGITS
    DIGITS = [DIGIT_0, DIGIT_1, DIGIT_2, DIGIT_3, DIGIT_4, DIGIT_5, DIGIT_6, DIGIT_7, DIGIT_8, DIGIT_9, BLANK_DIGIT]

    global POSITIONS
    POSITIONS = [POS_1, POS_2, POS_3, POS_4]
    
    displayOff()
    
    global start_time
    start_time = time.time()


def showPosition(position):
    positionsOff()
    position.on()

def displayDigit(digit):
    #start by turning off all the segments
    segmentsOff()
    
    for segment in digit:
        segment.off() # gpio "off" turns on the LED

def positionsOff():
    for pos in POSITIONS:
        pos.off()

def segmentsOff():
    for segment in DIGITS[8]:
        segment.on() # gpio "on" turns off the LED

def displayOff():
    # turn off all the digit positions
    positionsOff()
    # turn off all the segments
    segmentsOff()

def timeTick(timer):
    global time_left, start_time, TOTAL_TIME, colon_visible, flashing
    colon_visible =  not colon_visible
    
    if vcr_mode or time_left == 0 : return
    
    current_time = time.time() # using the time() function will likely be more accurate than counting the ticks, plus we can mess with the tick frequency now.
    elapsed_time = current_time - start_time
    time_left : int = TOTAL_TIME - elapsed_time
    
    if time_left <= 0 :
        time_left = 0
        flashing = True
        
    showTime(time_left)

def startSecondsTicker():
    Timer().init(freq=2, mode=Timer.PERIODIC, callback=timeTick)
    
def displayRefresh():
    global display_buffer, POSITIONS, colon_visible, divider_colon, flashing
    
    if not colon_visible and flashing:
        displayOff()
        divider_colon.on()  # turns the LEDs off
        return
    
    led_on_time = 0.0025
    # too much on time will cause flickering - the display must keep each position lit up just long enough to repeat it again fast enough to fool the eye.
    
    for index, position in enumerate(POSITIONS) :
        showPosition(position) 
        displayDigit(display_buffer[index])
        time.sleep(led_on_time)
        # You can use a bigger delay here for debugging purposes, to make it easier to see the digits being shown in turn.
        
    displayOff() # otherwise it will linger on the last digit, making it brighter than the rest
    
    if colon_visible :
        divider_colon.off() # turns the LEDs on
    
    time.sleep(led_on_time)
    divider_colon.on()  # turns the LEDs off

def showTwelve():
    display_buffer[0] = DIGITS[1]
    display_buffer[1] = DIGITS[2]
    display_buffer[2] = DIGITS[0]
    display_buffer[3] = DIGITS[0]

def showTime(total_seconds):
    global display_buffer

    #print("show time total seconds = ", total_seconds)
    
    if total_seconds <= 0 :
        display_buffer[0] = DIGITS[0]
        display_buffer[1] = DIGITS[0]
        display_buffer[2] = DIGITS[0]
        display_buffer[3] = DIGITS[0]
        return

    # convert seconds to hours, minutes, and seconds
    hours = total_seconds // 3600  # the // is "floor division" which avoids fractions
    leftover = total_seconds % 3600
    minutes = leftover // 60
    seconds = leftover % 60
    # print("time = ", hours, ":", minutes, ":", seconds)
    
    if hours > 0 :
        first_number = hours
        second_number = minutes
    else :
        first_number = minutes
        second_number = seconds

    if hours > 99 :
        display_buffer[0] = DIGITS[8]
        display_buffer[1] = DIGITS[8]
        display_buffer[2] = DIGITS[8]
        display_buffer[3] = DIGITS[8]
    else :
        if first_number > 9 :
            display_buffer[0] = DIGITS[first_number // 10]
        else :
            display_buffer[0] = BLANK_DIGIT
        
        display_buffer[1] = DIGITS[first_number % 10]
        display_buffer[2] = DIGITS[second_number // 10]
        display_buffer[3] = DIGITS[second_number % 10]
        

# Start main code
setup()
startSecondsTicker()

if vcr_mode :
    flashing = True
    showTwelve()
else :
    showTime(TOTAL_TIME)

# Using a Timer to refresh the display seems to cause it to hang after a while
# but using this loop we can go nuts refreshing the display with all the spare clock cycles

while True:
    displayRefresh()

Cheers,
-Nico

  • Sign in to reply

Top Comments

  • DAB
    DAB over 3 years ago +1
    Good point on the global variable issue with Python. Logically it makes sense, but us older programmers miss the ability to reach out and touch a variable as needed.
  • shabaz
    shabaz over 3 years ago in reply to ntewinkel

    I couldn't understand the reason either. I guess since it is interpreted, it just continues to run the code with the assumption that it's local. 

    Some IDEs may flag it up as a warning if the global is not declared on a variable that exists outside the function too. I see this in Spyder:

    image

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • ntewinkel
    ntewinkel over 3 years ago in reply to shabaz

    I think the worst part for me is that it just creates the new local variable if I forgot to mention that it's a global one. In that sense I wish it required proper declaration of variables.

    But it's fun to play with, like making mud-pies as a kid - things get messy but it's part of the game!!

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • aspork42
    aspork42 over 3 years ago

    NNNNOOOOOOOO!!!!

    lol - nice work

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 3 years ago

    Hi Nico,

    The global thing was confusing for me originally, mainly because the keyword global suggests (to me at least) that the variable is being declared/instantiated as global, whereas with Python the variable is automatically global without that syntax when it's not in a function, it's just that you need the global keyword to get write access to it. I wish they'd used a different keyword, like (say) use or unlock perhaps. Anyway, it's quite neat, but the syntax is definitely confusing initially.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • DAB
    DAB over 3 years ago

    Good point on the global variable issue with Python.

    Logically it makes sense, but us older programmers miss the ability to reach out and touch a variable as needed.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube