From a94a87ae9e26d6ec82fbd8085b02fe221c46c97b Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 15 May 2023 09:14:24 -0500 Subject: [PATCH 1/2] implement non_blocking_marquee --- adafruit_ht16k33/segments.py | 239 +++++++++++------- .../ht16k33_segments_non_blocking_marquee.py | 31 +++ 2 files changed, 172 insertions(+), 98 deletions(-) mode change 100755 => 100644 adafruit_ht16k33/segments.py create mode 100644 examples/ht16k33_segments_non_blocking_marquee.py diff --git a/adafruit_ht16k33/segments.py b/adafruit_ht16k33/segments.py old mode 100755 new mode 100644 index 8033996..99d778b --- a/adafruit_ht16k33/segments.py +++ b/adafruit_ht16k33/segments.py @@ -7,7 +7,7 @@ adafruit_ht16k33.segments ========================= """ - +import time from time import sleep from adafruit_ht16k33.ht16k33 import HT16K33 @@ -17,107 +17,106 @@ except ImportError: pass - __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_HT16K33.git" # fmt: off CHARS = ( - 0b00000000, 0b00000000, # - 0b01000000, 0b00000110, # ! - 0b00000010, 0b00100000, # " - 0b00010010, 0b11001110, # # - 0b00010010, 0b11101101, # $ - 0b00001100, 0b00100100, # % - 0b00100011, 0b01011101, # & - 0b00000100, 0b00000000, # ' - 0b00100100, 0b00000000, # ( - 0b00001001, 0b00000000, # ) - 0b00111111, 0b11000000, # * - 0b00010010, 0b11000000, # + - 0b00001000, 0b00000000, # , - 0b00000000, 0b11000000, # - - 0b00000000, 0b00000000, # . - 0b00001100, 0b00000000, # / - 0b00001100, 0b00111111, # 0 - 0b00000000, 0b00000110, # 1 - 0b00000000, 0b11011011, # 2 - 0b00000000, 0b10001111, # 3 - 0b00000000, 0b11100110, # 4 - 0b00100000, 0b01101001, # 5 - 0b00000000, 0b11111101, # 6 - 0b00000000, 0b00000111, # 7 - 0b00000000, 0b11111111, # 8 - 0b00000000, 0b11101111, # 9 - 0b00010010, 0b00000000, # : - 0b00001010, 0b00000000, # ; - 0b00100100, 0b01000000, # < - 0b00000000, 0b11001000, # = - 0b00001001, 0b10000000, # > - 0b01100000, 0b10100011, # ? - 0b00000010, 0b10111011, # @ - 0b00000000, 0b11110111, # A - 0b00010010, 0b10001111, # B - 0b00000000, 0b00111001, # C - 0b00010010, 0b00001111, # D - 0b00000000, 0b11111001, # E - 0b00000000, 0b01110001, # F - 0b00000000, 0b10111101, # G - 0b00000000, 0b11110110, # H - 0b00010010, 0b00000000, # I - 0b00000000, 0b00011110, # J - 0b00100100, 0b01110000, # K - 0b00000000, 0b00111000, # L - 0b00000101, 0b00110110, # M - 0b00100001, 0b00110110, # N - 0b00000000, 0b00111111, # O - 0b00000000, 0b11110011, # P - 0b00100000, 0b00111111, # Q - 0b00100000, 0b11110011, # R - 0b00000000, 0b11101101, # S - 0b00010010, 0b00000001, # T - 0b00000000, 0b00111110, # U - 0b00001100, 0b00110000, # V - 0b00101000, 0b00110110, # W - 0b00101101, 0b00000000, # X - 0b00010101, 0b00000000, # Y - 0b00001100, 0b00001001, # Z - 0b00000000, 0b00111001, # [ - 0b00100001, 0b00000000, # \ - 0b00000000, 0b00001111, # ] - 0b00001100, 0b00000011, # ^ - 0b00000000, 0b00001000, # _ - 0b00000001, 0b00000000, # ` - 0b00010000, 0b01011000, # a - 0b00100000, 0b01111000, # b - 0b00000000, 0b11011000, # c - 0b00001000, 0b10001110, # d - 0b00001000, 0b01011000, # e - 0b00000000, 0b01110001, # f - 0b00000100, 0b10001110, # g - 0b00010000, 0b01110000, # h - 0b00010000, 0b00000000, # i - 0b00000000, 0b00001110, # j - 0b00110110, 0b00000000, # k - 0b00000000, 0b00110000, # l - 0b00010000, 0b11010100, # m - 0b00010000, 0b01010000, # n - 0b00000000, 0b11011100, # o - 0b00000001, 0b01110000, # p - 0b00000100, 0b10000110, # q - 0b00000000, 0b01010000, # r - 0b00100000, 0b10001000, # s - 0b00000000, 0b01111000, # t - 0b00000000, 0b00011100, # u - 0b00100000, 0b00000100, # v - 0b00101000, 0b00010100, # w - 0b00101000, 0b11000000, # x - 0b00100000, 0b00001100, # y - 0b00001000, 0b01001000, # z - 0b00001001, 0b01001001, # { - 0b00010010, 0b00000000, # | - 0b00100100, 0b10001001, # } - 0b00000101, 0b00100000, # ~ + 0b00000000, 0b00000000, # + 0b01000000, 0b00000110, # ! + 0b00000010, 0b00100000, # " + 0b00010010, 0b11001110, # # + 0b00010010, 0b11101101, # $ + 0b00001100, 0b00100100, # % + 0b00100011, 0b01011101, # & + 0b00000100, 0b00000000, # ' + 0b00100100, 0b00000000, # ( + 0b00001001, 0b00000000, # ) + 0b00111111, 0b11000000, # * + 0b00010010, 0b11000000, # + + 0b00001000, 0b00000000, # , + 0b00000000, 0b11000000, # - + 0b00000000, 0b00000000, # . + 0b00001100, 0b00000000, # / + 0b00001100, 0b00111111, # 0 + 0b00000000, 0b00000110, # 1 + 0b00000000, 0b11011011, # 2 + 0b00000000, 0b10001111, # 3 + 0b00000000, 0b11100110, # 4 + 0b00100000, 0b01101001, # 5 + 0b00000000, 0b11111101, # 6 + 0b00000000, 0b00000111, # 7 + 0b00000000, 0b11111111, # 8 + 0b00000000, 0b11101111, # 9 + 0b00010010, 0b00000000, # : + 0b00001010, 0b00000000, # ; + 0b00100100, 0b01000000, # < + 0b00000000, 0b11001000, # = + 0b00001001, 0b10000000, # > + 0b01100000, 0b10100011, # ? + 0b00000010, 0b10111011, # @ + 0b00000000, 0b11110111, # A + 0b00010010, 0b10001111, # B + 0b00000000, 0b00111001, # C + 0b00010010, 0b00001111, # D + 0b00000000, 0b11111001, # E + 0b00000000, 0b01110001, # F + 0b00000000, 0b10111101, # G + 0b00000000, 0b11110110, # H + 0b00010010, 0b00000000, # I + 0b00000000, 0b00011110, # J + 0b00100100, 0b01110000, # K + 0b00000000, 0b00111000, # L + 0b00000101, 0b00110110, # M + 0b00100001, 0b00110110, # N + 0b00000000, 0b00111111, # O + 0b00000000, 0b11110011, # P + 0b00100000, 0b00111111, # Q + 0b00100000, 0b11110011, # R + 0b00000000, 0b11101101, # S + 0b00010010, 0b00000001, # T + 0b00000000, 0b00111110, # U + 0b00001100, 0b00110000, # V + 0b00101000, 0b00110110, # W + 0b00101101, 0b00000000, # X + 0b00010101, 0b00000000, # Y + 0b00001100, 0b00001001, # Z + 0b00000000, 0b00111001, # [ + 0b00100001, 0b00000000, # \ + 0b00000000, 0b00001111, # ] + 0b00001100, 0b00000011, # ^ + 0b00000000, 0b00001000, # _ + 0b00000001, 0b00000000, # ` + 0b00010000, 0b01011000, # a + 0b00100000, 0b01111000, # b + 0b00000000, 0b11011000, # c + 0b00001000, 0b10001110, # d + 0b00001000, 0b01011000, # e + 0b00000000, 0b01110001, # f + 0b00000100, 0b10001110, # g + 0b00010000, 0b01110000, # h + 0b00010000, 0b00000000, # i + 0b00000000, 0b00001110, # j + 0b00110110, 0b00000000, # k + 0b00000000, 0b00110000, # l + 0b00010000, 0b11010100, # m + 0b00010000, 0b01010000, # n + 0b00000000, 0b11011100, # o + 0b00000001, 0b01110000, # p + 0b00000100, 0b10000110, # q + 0b00000000, 0b01010000, # r + 0b00100000, 0b10001000, # s + 0b00000000, 0b01111000, # t + 0b00000000, 0b00011100, # u + 0b00100000, 0b00000100, # v + 0b00101000, 0b00010100, # w + 0b00101000, 0b11000000, # x + 0b00100000, 0b00001100, # y + 0b00001000, 0b01001000, # z + 0b00001001, 0b01001001, # { + 0b00010010, 0b00000000, # | + 0b00100100, 0b10001001, # } + 0b00000101, 0b00100000, # ~ 0b00111111, 0b11111111, ) # fmt: on @@ -189,6 +188,9 @@ def __init__( self._chars = chars_per_display * len(self.i2c_device) self._bytes_per_char = 2 + self._last_nb_scroll_time = -1 + self._nb_scroll_text = None + self._nb_scroll_index = -1 def print(self, value: Union[str, float], decimal: int = 0) -> None: """Print the value to the display. @@ -367,6 +369,47 @@ def set_digit_raw( if self._auto_write: self.show() + def non_blocking_marquee(self, text: str, delay: float = 0.25, loop: bool = True): + """ + Scroll the text at the specified delay between characters. Must be called + repeatedly from main loop faster than delay time. + + :param str text: The text to display + :param float delay: (optional) The delay in seconds to pause before scrolling + to the next character (default=0.25) + :param bool loop: (optional) Whether to endlessly loop the text (default=True) + """ + # pylint: disable=too-many-nested-blocks + if isinstance(text, str): + now = time.monotonic() + # if text is the same + if text == self._nb_scroll_text: + # if the text is 4 or less chars we don't need scrolling. + if len(text) > 4: + # if we delayed long enough, and it's time to scroll + if now >= self._last_nb_scroll_time + delay: + self._last_nb_scroll_time = now + # if there are chars left in the text + if self._nb_scroll_index + 1 < len(text): + self._nb_scroll_index += 1 + self._push(text[self._nb_scroll_index]) + self.show() + else: + if loop: + self._nb_scroll_index = -1 + self._push(" ") + self.show() + + else: + # different text + self._nb_scroll_text = text + self._last_nb_scroll_time = now + if len(text) <= 4: + self.print(f"{text}{' ' * (4 - len(text))}") + else: + self._nb_scroll_index = 3 + self.print(text[0:4]) + def marquee(self, text: str, delay: float = 0.25, loop: bool = True) -> None: """ Automatically scroll the text at the specified delay between characters @@ -398,7 +441,7 @@ def _scroll_marquee(self, text: str, delay: float) -> None: class _AbstractSeg7x4(Seg14x4): - POSITIONS = (0, 2, 6, 8) # The positions of characters. + POSITIONS = (0, 2, 6, 8) # The positions of characters. def __init__( # pylint: disable=too-many-arguments self, diff --git a/examples/ht16k33_segments_non_blocking_marquee.py b/examples/ht16k33_segments_non_blocking_marquee.py new file mode 100644 index 0000000..f519748 --- /dev/null +++ b/examples/ht16k33_segments_non_blocking_marquee.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2023 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT +""" +Example that uses Non-Blocking Marquee to scroll text on 14x4 segment +while also blinking the on-board neopixel at a different rate from the +marquee scrolling. +""" + +import time +import board +import neopixel +import adafruit_ht16k33.segments + + +i2c = board.I2C() +segment_display = adafruit_ht16k33.segments.Seg14x4(i2c) + +pixel_pin = board.NEOPIXEL +pixels = neopixel.NeoPixel(pixel_pin, 1, brightness=0.1, auto_write=True) + +pixels[0] = 0xFF0000 +last_blink = 0 +while True: + now = time.monotonic() + if now > last_blink + 0.3: + if pixels[0] == (255, 0, 255): + pixels[0] = 0x00FF00 + else: + pixels[0] = 0xFF00FF + last_blink = now + segment_display.non_blocking_marquee("CircuitPython <3", delay=0.2) From 2407c6e8b71106326ef4b6cd0e0575170f169146 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 2 Jun 2023 17:57:08 -0500 Subject: [PATCH 2/2] make non-blocking behavior match blocking, refactor to re-use non-blocking from blocking and delete now unused code. --- adafruit_ht16k33/segments.py | 85 +++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/adafruit_ht16k33/segments.py b/adafruit_ht16k33/segments.py index 99d778b..0b8e142 100644 --- a/adafruit_ht16k33/segments.py +++ b/adafruit_ht16k33/segments.py @@ -8,7 +8,6 @@ ========================= """ import time -from time import sleep from adafruit_ht16k33.ht16k33 import HT16K33 try: @@ -191,6 +190,7 @@ def __init__( self._last_nb_scroll_time = -1 self._nb_scroll_text = None self._nb_scroll_index = -1 + self._nb_prev_char_is_dot = False def print(self, value: Union[str, float], decimal: int = 0) -> None: """Print the value to the display. @@ -369,7 +369,13 @@ def set_digit_raw( if self._auto_write: self.show() - def non_blocking_marquee(self, text: str, delay: float = 0.25, loop: bool = True): + def non_blocking_marquee( + self, + text: str, + delay: float = 0.25, + loop: bool = True, + space_between: bool = False, + ) -> bool: """ Scroll the text at the specified delay between characters. Must be called repeatedly from main loop faster than delay time. @@ -378,39 +384,48 @@ def non_blocking_marquee(self, text: str, delay: float = 0.25, loop: bool = True :param float delay: (optional) The delay in seconds to pause before scrolling to the next character (default=0.25) :param bool loop: (optional) Whether to endlessly loop the text (default=True) + :param bool space_between: (optional) Whether to seperate the end and beginning of + the text with a space. (default=False) """ # pylint: disable=too-many-nested-blocks if isinstance(text, str): now = time.monotonic() # if text is the same if text == self._nb_scroll_text: - # if the text is 4 or less chars we don't need scrolling. - if len(text) > 4: - # if we delayed long enough, and it's time to scroll - if now >= self._last_nb_scroll_time + delay: - self._last_nb_scroll_time = now - # if there are chars left in the text - if self._nb_scroll_index + 1 < len(text): - self._nb_scroll_index += 1 - self._push(text[self._nb_scroll_index]) - self.show() + # if we delayed long enough, and it's time to scroll + if now >= self._last_nb_scroll_time + delay: + # if there are chars left in the text + if self._nb_scroll_index + 1 < len(text): + self._nb_scroll_index += 1 + + _character = text[self._nb_scroll_index] + + if _character != "." or self._nb_prev_char_is_dot: + self._last_nb_scroll_time = now + + self.print(text[self._nb_scroll_index]) + self._nb_prev_char_is_dot = text[self._nb_scroll_index] == "." + else: + self._nb_scroll_index = -1 + if loop: + if space_between: + self._last_nb_scroll_time = now + self.print(" ") else: - if loop: - self._nb_scroll_index = -1 - self._push(" ") - self.show() - + return True else: # different text + self._nb_scroll_index = 0 + self.fill(False) self._nb_scroll_text = text self._last_nb_scroll_time = now - if len(text) <= 4: - self.print(f"{text}{' ' * (4 - len(text))}") - else: - self._nb_scroll_index = 3 - self.print(text[0:4]) + self.print(text[0]) + + return False - def marquee(self, text: str, delay: float = 0.25, loop: bool = True) -> None: + def marquee( + self, text: str, delay: float = 0.25, loop: bool = True, space_between=False + ) -> None: """ Automatically scroll the text at the specified delay between characters @@ -418,26 +433,16 @@ def marquee(self, text: str, delay: float = 0.25, loop: bool = True) -> None: :param float delay: (optional) The delay in seconds to pause before scrolling to the next character (default=0.25) :param bool loop: (optional) Whether to endlessly loop the text (default=True) - + :param bool space_between: (optional) Whether to seperate the end and beginning of + the text with a space. (default=False) """ if isinstance(text, str): self.fill(False) - if loop: - while True: - self._scroll_marquee(text, delay) - else: - self._scroll_marquee(text, delay) - - def _scroll_marquee(self, text: str, delay: float) -> None: - """Scroll through the text string once using the delay""" - char_is_dot = False - for character in text: - self.print(character) - # Add delay if character is not a dot or more than 2 in a row - if character != "." or char_is_dot: - sleep(delay) - char_is_dot = character == "." - self.show() + while True: + if self.non_blocking_marquee( + text=text, delay=delay, loop=loop, space_between=space_between + ): + return class _AbstractSeg7x4(Seg14x4):