4 Digit 7 Segment Display Micro Python Example

Hi Guys,
I’m trying to use one of these to display 4 digits and a decimal point on my pico.

I can’t find a Micro python example to follow.
The one(s) I found here just display gibberish then the program hangs.

I used the following product a while pack on a Regular Raspberry PI project and that was fairly simple to get going.

I recall a lot of the hard work was done in the “Back Pack” bit and I only needed to connect something like 3 or 4 pins. It could display the numbers and it would keep doing that until I blanked them out with no continuous looping code required.

Have I purchased the wrong product?
Does anyone know of a working Micro python Example?

Thanks
David

Hey David,

This display is based on the 74HC595 shift register, you use SPI in order to load the value to display into that shift register.

I agree that the “example code” provided by Waveshare for this display is fairly sub-par compared to the Adafruit guide:

This “run the code in your editor” for the corresponding .py file is not especially useful

Under the hood, this HAT is also only using a few GPIO off the Pico, specifically 9, 10, and 11 as CS, CLK, and MOSI for SPI, renamed to RCLK, CLK, and DIN it seems?:

Here’s the demo code that they appear to be referring to, had to chase down the 7z for it:

from machine import Pin,SPI,PWM
import framebuf
import time

MOSI = 11
SCK = 10    
RCLK = 9

KILOBIT   = 0xFE
HUNDREDS  = 0xFD
TENS      = 0xFB
UNITS     = 0xF7
Dot       = 0x80

SEG8Code = [
    0x3F, # 0
    0x06, # 1
    0x5B, # 2
    0x4F, # 3
    0x66, # 4
    0x6D, # 5
    0x7D, # 6
    0x07, # 7
    0x7F, # 8
    0x6F, # 9
    0x77, # A
    0x7C, # b
    0x39, # C
    0x5E, # d
    0x79, # E
    0x71  # F
    ] 
class LED_8SEG():
    def __init__(self):
        self.rclk = Pin(RCLK,Pin.OUT)
        self.rclk(1)
        self.spi = SPI(1)
        self.spi = SPI(1,1000_000)
        self.spi = SPI(1,10000_000,polarity=0, phase=0,sck=Pin(SCK),mosi=Pin(MOSI),miso=None)
        self.SEG8=SEG8Code
    '''
    function: Send Command
    parameter: 
        Num: bit select
        Seg:segment select       
    Info:The data transfer
    '''
    def write_cmd(self, Num, Seg):    
        self.rclk(1)
        self.spi.write(bytearray([Num]))
        self.spi.write(bytearray([Seg]))
        self.rclk(0)
        time.sleep(0.002)
        self.rclk(1)

   
if __name__=='__main__':
    LED = LED_8SEG()
    #color BRG
    while(1):
        for o in range(99999):
            time.sleep(0.0005)
            LED.write_cmd(UNITS,LED.SEG8[o%10])
            time.sleep(0.0005)
            LED.write_cmd(TENS,LED.SEG8[(o%100)//10])
            time.sleep(0.0005)
            LED.write_cmd(HUNDREDS,LED.SEG8[(o%1000)//100]|Dot)
            time.sleep(0.0005)
            LED.write_cmd(KILOBIT,LED.SEG8[(o%10000)//1000])

Is this what you’ve attempted to run? What were your results?

1 Like

Hi Bryce,
The code I found was in a .7z file and when I look a bit closer it looks like it is intended for a LCD not a LED. I downloaded it from the the “Pico-8SEG-LED - Waveshare Wiki” link above.
The class within the code is named “LCD_2inch(framebuf.FrameBuffer):” so it must be linked to the wrong place. I understand it’s my lack of electronics knowledge that makes this more difficult.

I think your example is exactly what I am looking for. I should be able to chop that up to something that displays what I need. :slight_smile: Where did you find this? (or How did I miss it?)

I’ll have a play and see if I can figure out how to display what I want.
The demo seems to count nicely like a stopwatch and when I end the program a random number is left displayed on the right most position. Other posts indicated that I need to constantly refresh the display.
Is that because it only displays one digit at a time and refreshes so fast you can’t tell?

Thanks for your assistance
David

Hey David,

I found it here under “Demo Codes”:

https://files.waveshare.com/upload/9/94/Pico-8SEG-LED-Code.7z

image

Once you extract the 7z you can find it in this subfolder: \Pico-8SEG-LED-Code\Python\Pico-8SEG-LED above it is a compatible UF2 (I suspect just for the Pico, not the H) you should be able to use MicroPython latest with this anyway, they often prioritize keeping the low level libraries backwards compatible.

Should be fairly easy to reverse engineer the script. Seems it is just incrementing an integer o from 0 to 99999, taking the rounded modulo to get each position. Interesting how they’ve used KILOBIT instead of THOUSANDS even though for one it is a representation in base-10, so BIT has nothing to do with it and it breaks its own previous conventions, but if that’s an actual accurate trade term in this context I’d be curious if someone has heard of it, maybe it has something to do with numerical displays specifically :man_shrugging:

As long as you’re after this line

LED = LED_8SEG()

You should be free to use:

LED.write_cmd(UNIT_VALUE,  SEG_CODE)

To set the values for each unit, with 0x80 referencing the decimal place

1 Like

Not sure how I found the LCD zip. Doesn’t matter.
Thanks for the description, I’ll get into it later on.
I notice you mention the H suffix on the pico.
I thought that only meant it has pre soldered headers?
I understand the W model has a different Micro Python interpreter to handle the WIFI hardware.
Is there something else special about the Pico H.

1 Like

Hi David,

You’re spot on. The H is just for the headers, the same as W is for wireless.

1 Like

Easy to do with Waveshare Wiki.
They usually have all the stuff and make very good products, but some docs are not in obvious places.
I once spent days trying to figure something for one of their products; when the link was there all the time, it was not named logically and was at the very end of the web page.

Regards
Jim

2 Likes

Hi There,
I came up with this. It works no problem.
I post it here for the next guy. I will also post it on the product forum.
You call it like this.

displayNumber = '123.4'
displayTimer = 5   # Display time in seconds (Approximate)
displayLED(displayNumber,displayTimer)

It right justifies the number with a blank if less than 4 digits.
It displays and ‘E’ for error if something goes wrong.
I’m sure there are probably better ways to do this but it works :slight_smile:
I hope this helps someone else.
David

from machine import Pin,SPI,PWM
import framebuf
import time

MOSI = 11
SCK = 10    
RCLK = 9

THOUSANDS   = 0xFE
HUNDREDS  = 0xFD
TENS      = 0xFB
UNITS     = 0xF7
Dot       = 0x80

SEG8Code = [
    0x3F, # 0
    0x06, # 1
    0x5B, # 2
    0x4F, # 3
    0x66, # 4
    0x6D, # 5
    0x7D, # 6
    0x07, # 7
    0x7F, # 8
    0x6F, # 9
    0x77, # A (10)
    0x7C, # b (11)
    0x39, # C (12)
    0x5E, # d (13)
    0x79, # E (14)
    0x71, # F (15)
    0x00,  # Blank (16)
    0x00  # Decimal Point (17)
    ] 
class LED_8SEG():
    def __init__(self):
        self.rclk = Pin(RCLK,Pin.OUT)
        self.rclk(1)
        self.spi = SPI(1)
        self.spi = SPI(1,1000_000)
        self.spi = SPI(1,10000_000,polarity=0, phase=0,sck=Pin(SCK),mosi=Pin(MOSI),miso=None)
        self.SEG8=SEG8Code
    '''
    function: Send Command
    parameter: 
        Num: bit select
        Seg:segment select       
    Info:The data transfer
    '''
    def write_cmd(self, Num, Seg):    
        self.rclk(1)
        self.spi.write(bytearray([Num]))
        self.spi.write(bytearray([Seg]))
        self.rclk(0)
        time.sleep(0.002)
        self.rclk(1)
        
def decodeString(displayString):
    displayList=[]
    for character in displayString:
        if character == '0': code=0     
        if character == '1': code=1
        if character == '2': code=2     
        if character == '3': code=3     
        if character == '4': code=4     
        if character == '5': code=5     
        if character == '6': code=6    
        if character == '7': code=7     
        if character == '8': code=8     
        if character == '9': code=9     
        if character == 'A' or character == 'a' : code=10     
        if character == 'B' or character == 'b' : code=11     
        if character == 'C' or character == 'c' : code=12     
        if character == 'D' or character == 'd' : code=13     
        if character == 'E' or character == 'e' : code=14     
        if character == 'F' or character == 'f' : code=15     
        if character == ' ': code=16
        if character == '.': code=17
        if code != 17: displayList.append(code)
    return displayList

def decimalPlace(displayString):
    currentPosition=4
    for character in displayString:
        if character == '.':
            break
        currentPosition=currentPosition -1
    return currentPosition
    
def displayLED(displayNumber,displayTimer):
    try:
        outputDisplay(displayNumber,displayTimer)
    except:
        print("Error")
        LED=LED_8SEG()
        LED.write_cmd(THOUSANDS,LED.SEG8[14])    # Display E
        time.sleep(0.0005)
        
def outputDisplay(displayNumber,displayTimer):
    displayTimer = displayTimer* 110  # Convert timer to seconds
    displayString=str(displayNumber)

    # Left Pad with blanks
    noDecimal=displayString.replace('.','')    # Remove decimal point
    stringLength=len(noDecimal)
    if stringLength==3: displayString=' ' + displayString
    if stringLength==2: displayString='  ' + displayString
    if stringLength==1: displayString='   ' + displayString
    print("displayString="+displayString)
    
    displayList= decodeString(displayString)
    decimalPosition=decimalPlace(displayString)
    
    LED=LED_8SEG()        
    while displayTimer > 0:
        time.sleep(0.0005)
        if decimalPosition == 0:
            LED.write_cmd(UNITS,LED.SEG8[displayList[3]]|Dot)
        else:
            LED.write_cmd(UNITS,LED.SEG8[displayList[3]])
        time.sleep(0.0005)
        if decimalPosition == 1:
            LED.write_cmd(TENS,LED.SEG8[displayList[2]]|Dot)
        else:
            LED.write_cmd(TENS,LED.SEG8[displayList[2]])
        time.sleep(0.0005)
        if decimalPosition == 2:
            LED.write_cmd(HUNDREDS,LED.SEG8[displayList[1]]|Dot)
        else:
            LED.write_cmd(HUNDREDS,LED.SEG8[displayList[1]])
        time.sleep(0.0005)
        if decimalPosition == 3:
            LED.write_cmd(THOUSANDS,LED.SEG8[displayList[0]]|Dot)
        else:
            LED.write_cmd(THOUSANDS,LED.SEG8[displayList[0]])
        time.sleep(0.0005)
        displayTimer = displayTimer-1
        
    # Once finished blank out the fist left most digit
    # as it's the only one that sticks
    LED.write_cmd(UNITS,LED.SEG8[16])
    time.sleep(0.0005)


# TEST
displayNumber = '123.4'
displayTimer = 5   # Display time in seconds (Aproximate)
displayLED(displayNumber,displayTimer)
    

2 Likes

Nice work @David191372 :slight_smile:
This looks super good enough for most makers to get started with and often that’s the hardest part. Thanks for your contribution :clap: :partying_face: