# 7 Segment display test file # # By Tim from smbus2 import SMBus import time import math import random """ Each segment of the 14‑segment display is represented by a single bit in a 16‑bit integer. We use a dictionary to map segment names ("a", "b", ...) to their corresponding bitmask. Bitmask example: 1 << 0 = 0000 0000 0000 0001 1 << 1 = 0000 0000 0000 0010 1 << 2 = 0000 0000 0000 0100 """ SEG = { "a": 1 << 0, "b": 1 << 1, "c": 1 << 2, "d": 1 << 3, "e": 1 << 4, "f": 1 << 5, "g1": 1 << 6, "g2": 1 << 7, "h": 1 << 8, "i": 1 << 9, "j": 1 << 10, "k": 1 << 11, "l": 1 << 12, "m": 1 << 13, "dp": 1 << 14, } # A convenience list used by some effects ALL_SEGMENTS = list(SEG.values()) """ This function takes any number of segment names and returns a combined bitmask. It lets us write: seg("a", "b", "c") instead of manually OR-ing bits together. """ def seg(*names: str) -> int: value = 0 for n in names: value |= SEG[n] # bitwise OR return value """ FONT is a dictionary mapping characters ("A", "b", "3", etc.) to the bitmask that lights up the correct segments. This is a canonical HP‑style font, designed for clarity and legibility. """ FONT = { # punctuation " ": 0, "-": seg("g1","g2"), "_": seg("d"), ".": seg("dp"), ":": seg("i","l"), "!": seg("i","dp"), "?": seg("a","b","g2","k","dp"), "/": seg("j","m"), "\\": seg("h","k"), "'": seg("h"), "\"": seg("h","j"), ",": seg("m"), # digits "0": seg("a","b","c","d","e","f"), "1": seg("b","c"), "2": seg("a","b","g1","g2","e","d"), "3": seg("a","b","c","d","g1","g2"), "4": seg("f","g1","g2","b","c"), "5": seg("a","f","g1","g2","c","d"), "6": seg("a","f","e","d","c","g1","g2"), "7": seg("a","b","c"), "8": seg("a","b","c","d","e","f","g1","g2"), "9": seg("a","b","c","d","f","g1","g2"), # uppercase "A": seg("a","b","c","e","f","g1","g2"), "B": seg("a","j","m","g1","d","e","f"), "C": seg("a","f","e","d"), "D": seg("a","b","c","d","i","l"), "E": seg("a","f","e","d","g1","g2"), "F": seg("a","f","e","g1","g2"), "G": seg("a","f","e","d","c","g2"), "H": seg("f","e","b","c","g1","g2"), "I": seg("a","d","i","l"), "J": seg("b","c","d","e"), "K": seg("f","e","g1","j","m"), "L": seg("f","e","d"), "M": seg("f","e","b","c","h","j"), "N": seg("f","e","b","c","h","m"), "O": seg("a","b","c","d","e","f"), "P": seg("a","b","f","e","g1","g2"), "Q": seg("a","b","c","d","e","f","m"), "R": seg("a","b","f","e","g1","g2","m"), "S": seg("a","f","g1","g2","c","d"), "T": seg("a","i","l"), "U": seg("f","e","b","c","d"), "V": seg("f","e","j","k"), "W": seg("f","e","b","c","k","m"), "X": seg("h","j","k","m"), "Y": seg("b","f","g1","g2","l"), "Z": seg("a","j","k","d"), # lowercase "a": seg("l","g2","c","d"), "b": seg("f","e","d","c","g1","g2"), "c": seg("g1","g2","e","d"), "d": seg("b","c","d","e","g1","g2"), "e": seg("g1","g2","e","d","c"), "f": seg("a","f","e","g1"), "g": seg("m","c","d","g2"), "h": seg("f","e","g1","g2","c"), "i": seg("i","l"), "j": seg("b","c","d"), "k": seg("f","e","g1","j","k"), "l": seg("f","e"), "m": seg("e","l","c","h","j"), "n": seg("e","g1","g2","c"), "o": seg("g1","g2","c","d","e"), "p": seg("a","b","f","e","g1","g2"), "q": seg("a","b","c","d","g1","g2"), "r": seg("e","g1"), "s": seg("a","f","g1","g2","c","d"), "t": seg("f","e","d","g1"), "u": seg("e","c","d"), "v": seg("e","c","j"), "w": seg("e","c","k","m"), "x": seg("h","j","k","m"), "y": seg("h","g2","b","c","d"), "z": seg("a","b","d","e","k"), } # Helper to fetch glyphs def glyph_for(ch: str, dot=False) -> int: value = FONT.get(ch, 0) if dot: value |= SEG["dp"] return value """ This class represents the entire display system. A class bundles: • data (the display buffer) • behavior (methods like scroll_text, chase, rainbow) Every method that begins with "self" operates on the display instance. """ class StarburstHT16K33: # Constructor: runs when you create the object def __init__(self, bus=1, addr0=0x70, addr1=0x71, brightness=15): """ __init__ is the constructor. It sets up the I²C bus and initializes the display chips. """ self.bus = SMBus(bus) self.addr = [addr0, addr1] # two HT16K33 chips self.buffer = [0] * 8 # 8 digits self.brightness = max(0, min(15, brightness)) # Initialize each chip for ht16k55_addr in self.addr: self._cmd(ht16k55_addr, 0x21) # turn on oscillator self._cmd(ht16k55_addr, 0xE0 | self.brightness) self._cmd(ht16k55_addr, 0x81) # display on # Low-level I²C helpers def _cmd(self, address, cmd): """Send a single command byte to the chip.""" self.bus.write_byte(address, cmd) def _write_digit_block(self, address, values): """ Write 4 digits (8 bytes) to one HT16K33 chip. Each digit is 16 bits, so we split into two bytes. """ data = [] for v in values: data.append(v & 0xFF) # low byte data.append((v >> 8) & 0xFF) # high byte self.bus.write_i2c_block_data(address, 0x00, data) # Basic display operations def set_brightness(self, brightness): """Set brightness 0–15.""" self.brightness = max(0, min(15, brightness)) for a in self.addr: self._cmd(a, 0xE0 | self.brightness) def set_char(self, pos, ch, dot=False): """Write a single character to a position.""" if 0 <= pos < 8: self.buffer[pos] = glyph_for(ch, dot) def set_raw(self, pos, mask): """Write a raw bitmask to a digit.""" if 0 <= pos < 8: self.buffer[pos] = mask def clear(self): """Clear the display buffer.""" for i in range(8): self.buffer[i] = 0 def show(self): """Send the buffer to the hardware.""" self._write_digit_block(self.addr[0], self.buffer[0:4]) self._write_digit_block(self.addr[1], self.buffer[4:8]) # Static text writer def write_text(self, text: str, align: str = "left"): """ Write up to 8 characters of text. Demonstrates: • slicing • alignment logic • enumerate() """ text = text[:8] self.clear() if align == "right": start = max(0, 8 - len(text)) elif align == "center": start = max(0, (8 - len(text)) // 2) else: start = 0 for i, ch in enumerate(text): self.set_char(start + i, ch) self.show() # Animated scrolling text with easing curves def scroll_text(self, text, scroll_speed=8, pulse=False, loop=False, easing="linear"): """ Demonstrates: • nested functions • closures • easing curves • animation loops • time-based effects """ scroll_speed = max(0, min(15, scroll_speed)) base_delay = 0.35 * (0.85 ** scroll_speed) # Local function: easing curve def ease(t): if easing == "linear": return t if easing == "in": return t * t if easing == "out": return 1 - (1 - t)**2 if easing == "inout": return 0.5 * (1 - math.cos(math.pi * t)) return t pad = " " * 8 s = pad + text + pad n = len(s) pulse_phase = 0.0 pulse_step = 0.25 + (scroll_speed / 60) base_brightness = self.brightness while True: for offset in range(n - 7): t = offset / (n - 8) if n > 8 else 0 delay = base_delay * (0.5 + ease(t)) window = s[offset:offset + 8] self.clear() for i, ch in enumerate(window): self.set_char(i, ch) self.show() if pulse: pulse_phase += pulse_step wave = 0.5 * (1 - math.cos(pulse_phase)) b = 1 + int(wave * 14) self.set_brightness(b) time.sleep(delay) if not loop: break self.set_brightness(base_brightness) # Marquee wrapper def marquee(self, text, scroll_speed=8, pulse=False, cycles=1, easing="linear"): """Just calls scroll_text() multiple times.""" for _ in range(cycles): self.scroll_text(text, scroll_speed, pulse, False, easing) # Per-segment chase effect def chase(self, speed=8, cycles=2): """Lights one segment at a time across all digits.""" delay = 0.15 * (0.85 ** speed) for _ in range(cycles): for segmask in ALL_SEGMENTS: for pos in range(8): self.buffer[pos] = segmask self.show() time.sleep(delay) # Rainbow brightness sweep def rainbow(self, cycles=2, speed=8): """Brightness follows a sine wave.""" steps = 60 delay = 0.05 * (0.85 ** speed) for _ in range(cycles): for i in range(steps): phase = i / steps b = 1 + int(14 * 0.5 * (1 - math.cos(2 * math.pi * phase))) self.set_brightness(b) self.show() time.sleep(delay) # Sparkle effect def sparkle(self, duration=2.0, density=0.15, speed=8): """Random segments flicker like stars.""" delay = 0.05 * (0.85 ** speed) end = time.time() + duration while time.time() < end: self.clear() for pos in range(8): if random.random() < density: self.buffer[pos] = random.choice(ALL_SEGMENTS) self.show() time.sleep(delay) def main(): """ Demonstrates: • object creation • calling methods • sequencing animations """ disp = StarburstHT16K33(brightness=10) disp.write_text("HELLO") time.sleep(1) disp.scroll_text("Learning Python!", scroll_speed=1, easing="inout", pulse=True) disp.marquee("HP FONT DEMO ", scroll_speed=8, cycles=2) disp.marquee("Learning Python! ", scroll_speed=1, cycles=5) disp.chase(speed=8) disp.write_text("RAINBOW") disp.rainbow() disp.write_text("SPARKLE") disp.sparkle(duration=2.0) disp.write_text("DONE") time.sleep(1) disp.clear() disp.show() if __name__ == "__main__": main()