014 - MicroPython TechNotes: 8x8 Dot Matrix Display (I2C)

Introduction

In this article, we will learn on how to use the 8×8 Dot Matrix display with an I2C communication interface using the MicroPython language.

Bill Of Materials

  1. ESP32 development board.
  2. Gorillacell ESP32 shield.
  3. 4-pin female-female dupont jumper wires.
  4. 8×8 Dot Matrix display (I2C)

Pinout

  1. GND – for the ground pin.
  2. VCC – for the supply voltage.
  3. SDA – for the I2C serial data pin.
  4. SCL – for the I2C serial clock pin.

Hardware Instruction

  1. Attach the ESP32 dev board on top of the ESP32 shield and make sure that both USB port are on the same side.
  2. Attach the dupont wire on the 8×8 Dot Matrix display by following the color coding which is black for the GND, red for the VCC, yellow for the SDA, and white for the SCL pin.
  3. Attach the other side of the dupont to the ESP32 shield by matching the colors of the wires to the colors of the pin headers that is black to black, red to red, and yellow and the following colors to yellow pin headers.
  4. Power the ESP32 shield by attaching an external power supply with a USB type-C connector and make sure that the power switch is slide to the ON state.
  5. Connect the ESP32 to the computer through a micro USB cable. Our demo circuit should now be ready.

Software Instruction

  1. Go to the source of the driver library from the Github of Tony Smith: https://github.com/smittytone/HT16K33-Python or you may copy it provided below on the SOURCE CODE section of this blog post.
  2. Save one by one the ht16k33.py and the ht16k33Matrix.py to the MicroPython device root directory.
    By clicking the File menu and select Save As.
  3. Select MicroPython device and name it as ht16k33.py and ht16k33Matrix accordingly.
  4. Enjoy playing with the examples and try to modify it to enhance the learning.

Video Demonstration

Call To Action

For any concern, write your message in the comment section.

You might also like to support my journey on Youtube by Subscribing. Click this to Subscribe to TechToTinker.

Thank you and have a good days ahead.

See you,

– George Bantique | tech.to.tinker@gmail.com

Source Code

1. Example # 1, exploring the basics:

  1# More details can be found in TechToTinker.blogspot.com 
  2# George Bantique | tech.to.tinker@gmail.com
  3
  4from machine import I2C, Pin, RTC
  5from ht16k33matrix import HT16K33Matrix
  6from time import sleep
  7
  8i2c = I2C(scl=Pin(22), sda=Pin(21))
  9display = HT16K33Matrix(i2c)
 10display.set_brightness(10)
 11display.set_angle(270) # properly set the orientation of the display
 12
 13# # The following lines of code should be tested using the REPL:
 14# # 1. To display a text:
 15# display.set_character(ord('A'), True).draw()
 16#
 17# # 2. To display all characters:
 18# for char in range(32, 128, 1):
 19#     display.clear().draw()
 20#     display.set_character(char, True).draw()
 21#     sleep(0.5)
 22#
 23# # 3. To display a scrolling text:
 24# display.clear().draw()
 25# text = "    abcdefghijklmnopqrstuvwxyz 0123456789!$%&*() x00x01    "
 26# display.scroll_text(text)
 27#
 28# # 4. Draw a custom icon on the LED
 29# icon = b"x3Cx42xA9x85x85xA9x42x3C"
 30# display.set_icon(icon).draw()
 31# # Rotate the icon
 32# display.set_angle(0).draw()
 33#
 34# # 5. Clear the LED
 35# display.clear().draw()
 36# 
 37# # 6. Record two custom icons using 'define_character()'
 38# icon = b"x0Ex18xBEx6Dx3Dx3C"
 39# display.define_character(icon, 0)
 40# icon = b"x3Cx3Dx6DxBEx18x0E"
 41# display.define_character(icon, 1)
 42# # Show the previously stored custom icon then Blink the LED
 43# display.set_character(0, True).draw()
 44# display.set_blink_rate(1)
 45# 
 46# # 7. Inverse the pixel
 47# display.set_inverse().draw()
 48# # Inverse the pixels (to revert)
 49# display.set_inverse().draw()
 50# 
 51# # 8. Clear and stop blinking
 52# display.clear().draw()
 53# display.set_blink_rate(0)
 54# 
 55# # 9. Plot an X
 56# for i in range(4):
 57#     display.plot(i, i).plot(7 - i, i).plot(i, 7 - i).plot(7 - i, 7 - i)
 58# display.draw()
 59# time.sleep(PAUSE)
 60# assert (display.is_set(0, 0) is True) and (display.is_set(0, 1) is False)
 61# display.clear().draw()
 62# 
 63# # 10. Show an animation
 64# while True:
 65#     x = 7
 66#     y = 0;
 67#     dx = 0
 68#     dy = 1;
 69#     mx = 6
 70#     my = 7;
 71#     nx = 0
 72#     ny = 0;
 73# 
 74#     for i in range(0,64):
 75#         display.plot(x, y).draw();
 76# 
 77#         if dx == 1 and x == mx:
 78#             dy = 1;
 79#             dx = 0;
 80#             mx -= 1;
 81#         elif dx == -1 and x == nx:
 82#             nx += 1;
 83#             dy = -1;
 84#             dx = 0;
 85#         elif dy == 1 and y == my:
 86#             dy = 0;
 87#             dx = -1;
 88#             my -= 1;
 89#         elif dy == -1 and y == ny:
 90#             dx = 1;
 91#             dy = 0;
 92#             ny += 1;
 93# 
 94#         x += dx;
 95#         y += dy
 96# 
 97#         sleep(0.01)
 98# 
 99#     x = 4
100#     y = 3
101#     dx = -1
102#     dy = 0
103#     mx = 5
104#     my = 4
105#     nx = 3
106#     ny = 2
107# 
108#     for i in range(0, 64):
109#         display.plot(x, y, 0).draw()
110# 
111#         if dx == 1 and x == mx:
112#             dy = -1;
113#             dx = 0;
114#             mx += 1;
115#         elif dx == -1 and x == nx:
116#             nx -= 1;
117#             dy = 1;
118#             dx = 0;
119#         elif dy == 1 and y == my:
120#             dy = 0;
121#             dx = 1;
122#             my += 1;
123#         elif dy == -1 and y == ny:
124#             dx = -1;
125#             dy = 0;
126#             ny -= 1;
127# 
128#         x += dx;
129#         y += dy
130# 
131#         sleep(0.01)

2. Example # 2, binary clock example:

 1# More details can be found in TechToTinker.blogspot.com 
 2# George Bantique | tech.to.tinker@gmail.com
 3
 4from machine import I2C, Pin, RTC
 5from ht16k33matrix import HT16K33Matrix
 6from time import sleep
 7
 8i2c = I2C(scl=Pin(22), sda=Pin(21))
 9display = HT16K33Matrix(i2c)
10display.set_brightness(10)
11display.set_angle(90) # properly set the orientation of the display
12display.clear().draw()
13
14rtc = RTC() 
15rtc.datetime((2021, 2, 24, 3, 20, 25, 0, 0)) 
16# rtc.datetime((YYYY, MM, DD, WD, HH, MM, SS, MS)) 
17# WD 1 = Monday 
18# WD 7 = Sunday
19
20def display_binary(decimal, column):
21    # converts decimal number into 8-bit binary
22    binary_str = '{0:8b}'.format(decimal)
23    #print(binary_str)
24    for row in range(0, 8):
25        if binary_str[row] == '1':
26            display.plot(column, row, 1)
27        else:
28            display.plot(column, row, 0)
29
30while True:
31    t = rtc.datetime()
32    #display_binary(decimal value, dot matrix column)
33    display_binary(t[0] % 100, 7)    # year
34    display_binary(t[1], 6)          # month
35    display_binary(t[2], 5)          # day
36    display_binary(t[4], 3)          # hour
37    display_binary(t[5], 2)          # minutes
38    display_binary(t[6], 1)          # seconds
39    display_binary(t[7] // 10000, 0) # subseconds
40    display.draw() # update the dot matrix display
41    sleep(0.0001)  # 100ms wait

3. ht16k33.py base driver library:

  1# MIT License
  2# 
  3# Copyright (c) 2020 Tony Smith (@smittytone)
  4# 
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11# 
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14# 
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22class HT16K33:
 23    """
 24    A simple, generic driver for the I2C-connected Holtek HT16K33 controller chip.
 25    This release supports MicroPython and CircuitPython
 26    Version:    3.0.2
 27    Bus:        I2C
 28    Author:     Tony Smith (@smittytone)
 29    License:    MIT
 30    Copyright:  2020
 31    """
 32
 33    # *********** CONSTANTS **********
 34
 35    HT16K33_GENERIC_DISPLAY_ON = 0x81
 36    HT16K33_GENERIC_DISPLAY_OFF = 0x80
 37    HT16K33_GENERIC_SYSTEM_ON = 0x21
 38    HT16K33_GENERIC_SYSTEM_OFF = 0x20
 39    HT16K33_GENERIC_DISPLAY_ADDRESS = 0x00
 40    HT16K33_GENERIC_CMD_BRIGHTNESS = 0xE0
 41    HT16K33_GENERIC_CMD_BLINK = 0x81
 42
 43    # *********** PRIVATE PROPERTIES **********
 44
 45    i2c = None
 46    address = 0
 47    brightness = 15
 48    flash_rate = 0
 49
 50    # *********** CONSTRUCTOR **********
 51
 52    def __init__(self, i2c, i2c_address):
 53        assert 0x00 <= i2c_address < 0x80, "ERROR - Invalid I2C address in HT16K33()"
 54        self.i2c = i2c
 55        self.address = i2c_address
 56        self.power_on()
 57
 58    # *********** PUBLIC METHODS **********
 59
 60    def set_blink_rate(self, rate=0):
 61        """
 62        Set the display's flash rate.
 63        Only four values (in Hz) are permitted: 0, 2, 1, and 0,5.
 64        Args:
 65            rate (int): The chosen flash rate. Default: 0Hz (no flash).
 66        """
 67        assert rate in (0, 0.5, 1, 2), "ERROR - Invalid blink rate set in set_blink_rate()"
 68        self.blink_rate = rate & 0x03
 69        self._write_cmd(self.HT16K33_GENERIC_CMD_BLINK | rate << 1)
 70
 71    def set_brightness(self, brightness=15):
 72        """
 73        Set the display's brightness (ie. duty cycle).
 74        Brightness values range from 0 (dim, but not off) to 15 (max. brightness).
 75        Args:
 76            brightness (int): The chosen flash rate. Default: 15 (100%).
 77        """
 78        if brightness < 0 or brightness > 15: brightness = 15
 79        self.brightness = brightness
 80        self._write_cmd(self.HT16K33_GENERIC_CMD_BRIGHTNESS | brightness)
 81
 82    def draw(self):
 83        """
 84        Writes the current display buffer to the display itself.
 85        Call this method after updating the buffer to update
 86        the LED itself.
 87        """
 88        self._render()
 89
 90    def update(self):
 91        """
 92        Alternative for draw() for backwards compatibility
 93        """
 94        self._render()
 95
 96    def clear(self):
 97        """
 98        Clear the buffer.
 99        Returns:
100            The instance (self)
101        """
102        for i in range(0, len(self.buffer)): self.buffer[i] = 0x00
103        return self
104
105    def power_on(self):
106        """
107        Power on the controller and display.
108        """
109        self._write_cmd(self.HT16K33_GENERIC_SYSTEM_ON)
110        self._write_cmd(self.HT16K33_GENERIC_DISPLAY_ON)
111
112    def power_off(self):
113        """
114        Power on the controller and display.
115        """
116        self._write_cmd(self.HT16K33_GENERIC_DISPLAY_OFF)
117        self._write_cmd(self.HT16K33_GENERIC_SYSTEM_OFF)
118
119    # ********** PRIVATE METHODS **********
120
121    def _render(self):
122        """
123        Write the display buffer out to I2C
124        """
125        buffer = bytearray(len(self.buffer) + 1)
126        buffer[1:] = self.buffer
127        buffer[0] = 0x00
128        self.i2c.writeto(self.address, bytes(buffer))
129
130    def _write_cmd(self, byte):
131        """
132        Writes a single command to the HT16K33. A private method.
133        """
134        self.i2c.writeto(self.address, bytes([byte]))

4. ht16k33Matrix.py driver library class for the Dot Matrix Display:

  1# MIT License
  2# 
  3# Copyright (c) 2020 Tony Smith (@smittytone)
  4# 
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11# 
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14# 
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22from ht16k33 import HT16K33
 23
 24class HT16K33Matrix(HT16K33):
 25    """
 26    Micro/Circuit Python class for the Adafruit 8x8 monochrome LED matrix
 27    backpack.
 28    Version:    3.0.2
 29    Bus:        I2C
 30    Author:     Tony Smith (@smittytone)
 31    License:    MIT
 32    Copyright:  2020
 33    """
 34
 35    # *********** CONSTANTS **********
 36
 37    CHARSET = [
 38        b"x00x00",              # space - Ascii 32
 39        b"xfa",                  # !
 40        b"xc0x00xc0",          # "
 41        b"x24x7ex24x7ex24",  # #
 42        b"x24xd4x56x48",      # $
 43        b"xc6xc8x10x26xc6",  # %
 44        b"x6cx92x6ax04x0a",  # &
 45        b"xc0",                  # '
 46        b"x7cx82",              # (
 47        b"x82x7c",              # )
 48        b"x10x7cx38x7cx10",  # *
 49        b"x10x10x7cx10x10",  # +
 50        b"x06x07",              # ,
 51        b"x10x10x10x10",      # -
 52        b"x06x06",              # .
 53        b"x04x08x10x20x40",  # /
 54        b"x7cx8ax92xa2x7c",  # 0 - Ascii 48
 55        b"x42xfex02",          # 1
 56        b"x46x8ax92x92x62",  # 2
 57        b"x44x92x92x92x6c",  # 3
 58        b"x18x28x48xfex08",  # 4
 59        b"xf4x92x92x92x8c",  # 5
 60        b"x3cx52x92x92x8c",  # 6
 61        b"x80x8ex90xa0xc0",  # 7
 62        b"x6cx92x92x92x6c",  # 8
 63        b"x60x92x92x94x78",  # 9
 64        b"x36x36",              # : - Ascii 58
 65        b"x36x37",              #
 66        b"x10x28x44x82",      # <
 67        b"x24x24x24x24x24",  # =
 68        b"x82x44x28x10",      # >
 69        b"x60x80x9ax90x60",  # ?
 70        b"x7cx82xbaxaax78",  # @
 71        b"x7ex90x90x90x7e",  # A - Ascii 65
 72        b"xfex92x92x92x6c",  # B
 73        b"x7cx82x82x82x44",  # C
 74        b"xfex82x82x82x7c",  # D
 75        b"xfex92x92x92x82",  # E
 76        b"xfex90x90x90x80",  # F
 77        b"x7cx82x92x92x5c",  # G
 78        b"xfex10x10x10xfe",  # H
 79        b"x82xfex82",          # I
 80        b"x0cx02x02x02xfc",  # J
 81        b"xfex10x28x44x82",  # K
 82        b"xfex02x02x02",      # L
 83        b"xfex40x20x40xfe",  # M
 84        b"xfex40x20x10xfe",  # N
 85        b"x7cx82x82x82x7c",  # O
 86        b"xfex90x90x90x60",  # P
 87        b"x7cx82x92x8cx7a",  # Q
 88        b"xfex90x90x98x66",  # R
 89        b"x64x92x92x92x4c",  # S
 90        b"x80x80xfex80x80",  # T
 91        b"xfcx02x02x02xfc",  # U
 92        b"xf8x04x02x04xf8",  # V
 93        b"xfcx02x3cx02xfc",  # W
 94        b"xc6x28x10x28xc6",  # X
 95        b"xe0x10x0ex10xe0",  # Y
 96        b"x86x8ax92xa2xc2",  # Z - Ascii 90
 97        b"xfex82x82",          # [
 98        b"x40x20x10x08x04",  # 
 99        b"x82x82xfe",          # ]
100        b"x20x40x80x40x20",  # ^
101        b"x02x02x02x02x02",  # _
102        b"xc0xe0",              # '
103        b"x04x2ax2ax1e",      # a - Ascii 97
104        b"xfex22x22x1c",      # b
105        b"x1cx22x22x22",      # c
106        b"x1cx22x22xfc",      # d
107        b"x1cx2ax2ax10",      # e
108        b"x10x7ex90x80",      # f
109        b"x18x25x25x3e",      # g
110        b"xfex20x20x1e",      # h
111        b"xbcx02",              # i
112        b"x02x01x21xbe",      # j
113        b"xfex08x14x22",      # k
114        b"xfcx02",              # l
115        b"x3ex20x18x20x1e",  # m
116        b"x3ex20x20 x1e",     # n
117        b"x1cx22x22x1c",      # o
118        b"x3fx22x22x1c",      # p
119        b"x1cx22x22x3f",      # q
120        b"x22x1ex20x10",      # r
121        b"x12x2ax2ax04",      # s
122        b"x20x7cx22x04",      # t
123        b"x3cx02x02x3e",      # u
124        b"x38x04x02x04x38",  # v
125        b"x3cx06x0cx06x3c",  # w
126        b"x22x14x08x14x22",  # x
127        b"x39x05x06x3c",      # y
128        b"x26x2ax2ax32",      # z - Ascii 122
129        b"x10x7cx82x82",      #
130        b"xee",                  # |
131        b"x82x82x7cx10",      #
132        b"x40x80x40x80",      # ~
133        b"x60x90x90x60"       # Degrees sign - Ascii 127
134    ]
135
136    # ********** PRIVATE PROPERTIES **********
137
138    width = 8
139    height = 8
140    def_chars = None
141    rotation_angle = 0
142    is_rotated = False
143    is_inverse = False
144
145    # *********** CONSTRUCTOR **********
146
147    def __init__(self, i2c, i2c_address=0x70):
148        self.buffer = bytearray(self.width)
149        self.def_chars = []
150        for i in range(32): self.def_chars.append(b"x00")
151        super(HT16K33Matrix, self).__init__(i2c, i2c_address)
152
153    # *********** PUBLIC METHODS **********
154
155    def set_angle(self, angle=0):
156        """
157        Set the matrix orientation.
158        Args:
159            angle (integer) Display auto-rotation angle, 0 to -360 degrees. Default: 0
160        Returns:
161            The instance (self)
162        """
163        # Bring the supplied angle to with 0-360 degrees
164        if angle > 360:
165            while angle > 360:
166                angle -= 360
167
168        if angle < 0:
169            while angle < 360:
170                angle += 360
171
172        # Convert angle to internal value:
173        # 0 = none, 1 = 90 clockwise, 2 = 180, 3 = 90 anti-clockwise
174        if angle > 3:
175            if angle < 45 or angle > 360: angle = 0
176            if angle >= 45 and angle < 135: angle = 1
177            if angle >= 135 and angle < 225: angle = 2
178            if angle >= 225: angle = 3
179
180        self.rotation_angle = angle
181        self.is_rotated = True if self.rotation_angle != 0 else False
182        return self
183
184    def set_inverse(self):
185        """
186        Inverts the ink colour of the display.
187        Returns:
188            The instance (self)
189        """
190        self.is_inverse = not self.is_inverse
191        for i in range(self.width):
192            self.buffer[i] = (~ self.buffer[i]) & 0xFF
193        return self
194
195    def set_icon(self, glyph, centre=False):
196        """
197        Displays a custom character on the matrix.
198        Args:
199            glyph (array) 1-8 8-bit values defining a pixel image. The data is passed as columns
200                          0 through 7, left to right. Bit 0 is at the bottom, bit 7 at the top
201            centre (bool) Whether the icon should be displayed centred on the screen. Default: False
202        Returns:
203            The instance (self)
204        """
205        length = len(glyph)
206        assert 0 < length <= self.width, "ERROR - Invalid glyph set in set_icon()"
207        for i in range(length):
208            a = i
209            if centre: a = i + ((8 - length) >> 1)
210            self.buffer[a] = glyph[i] if self.is_inverse is False else ((~ glyph[i]) & 0xFF)
211        return self
212
213    def set_character(self, ascii_value=32, centre=False):
214        """
215        Display a single character specified by its Ascii value on the matrix.
216        Args:
217            ascii_value (integer) Character Ascii code. Default: 32 (space)
218            centre (bool)         Whether the icon should be displayed centred on the screen. Default: False
219        Returns:
220            The instance (self)
221        """
222        assert 0 <= ascii_value < 128, "ERROR - Invalid ascii code set in set_character()"
223        glyph = None
224        if ascii_value < 32:
225            # A user-definable character has been chosen
226            glyph = self.def_chars[ascii_value]
227        else:
228            # A standard character has been chosen
229            ascii_value -= 32
230            if ascii_value < 0 or ascii_value >= len(self.CHARSET): ascii_value = 0
231            glyph = self.CHARSET[ascii_value]
232        return self.set_icon(glyph, centre)
233
234    def scroll_text(self, the_line, speed=0.1):
235        """
236        Scroll the specified line of text leftwards across the display.
237        Args:
238            the_line (string) The string to display
239            speed (float)     The delay between frames
240        Returns:
241            The instance (self)
242        """
243        import time
244
245        assert len(the_line) > 0, "ERROR - Invalid string set in scroll_text()"
246
247        # Calculate the source buffer size
248        length = 0
249        for i in range(0, len(the_line)):
250            asc_val = ord(the_line[i])
251            if asc_val < 32:
252                glyph = self.def_chars[asc_val]
253            else:
254                glyph = self.CHARSET[asc_val - 32]
255            length += len(glyph)
256            if asc_val > 32: length += 1
257        src_buffer = bytearray(length)
258
259        # Draw the string to the source buffer
260        row = 0
261        for i in range(0, len(the_line)):
262            asc_val = ord(the_line[i])
263            if asc_val < 32:
264                glyph = self.def_chars[asc_val]
265            else:
266                glyph = self.CHARSET[asc_val - 32]
267            for j in range(0, len(glyph)):
268                src_buffer[row] = glyph[j] if self.is_inverse is False else ((~ glyph[j]) & 0xFF)
269                row += 1
270            if asc_val > 32: row += 1
271        assert row == length, "ERROR - Mismatched lengths in scroll_text()"
272
273        # Finally, animate the line
274        cursor = 0
275        while True:
276            a = cursor
277            for i in range(0, self.width):
278                self.buffer[i] = src_buffer[a];
279                a += 1
280            self.draw()
281            cursor += 1
282            if cursor > length - self.width: break
283            time.sleep(speed)
284
285    def define_character(self, glyph, char_code=0):
286        """
287        Set a user-definable character for later use.
288        Args:
289            glyph (bytearray)   1-8 8-bit values defining a pixel image. The data is passed as columns,
290                                with bit 0 at the bottom and bit 7 at the top
291            char_code (integer) Character's ID Ascii code 0-31. Default: 0
292        Returns:
293            The instance (self)
294        """
295        assert 0 < len(glyph) <= self.width, "ERROR - Invalid glyph set in define_character()"
296        assert 0 <= char_code < 32, "ERROR - Invalid character code set in define_character()"
297        self.def_chars[char_code] = glyph
298        return self
299
300    def plot(self, x, y, ink=1, xor=False):
301        """
302        Plot a point on the matrix. (0,0) is bottom left as viewed.
303        Args:
304            x (integer)   X co-ordinate (0 - 7) left to right
305            y (integer)   Y co-ordinate (0 - 7) bottom to top
306            ink (integer) Pixel color: 1 = 'white', 0 = black. NOTE inverse video mode reverses this. Default: 1
307            xor (bool)    Whether an underlying pixel already of color ink should be inverted. Default: False
308        Returns:
309            The instance (self)
310        """
311        # Check argument range and value
312        assert (0 <= x < self.width) and (0 <= y < self.height), "ERROR - Invalid coordinate set in plot()"
313        if ink not in (0, 1): ink = 1
314        if ink == 1:
315            if self.is_set(x ,y) and xor:
316                self.buffer[x] ^= (1 << y)
317            else:
318                if self.buffer[x] & (1 << y) == 0: self.buffer[x] |= (1 << y)
319        else:
320            if not self.is_set(x ,y) and xor:
321                self.buffer[x] ^= (1 << y)
322            else:
323                if self.buffer[x] & (1 << y) != 0: self.buffer[x] &= ~(1 << y)
324        return self
325
326    def is_set(self, x, y):
327        """
328        Indicate whether a pixel is set.
329        Args:
330            x (int) X co-ordinate left to right
331            y (int) Y co-ordinate bottom to top
332        Returns:
333            Whether the pixel is set (True) or not (False)
334        """
335        assert (0 <= x < self.width) and (0 <= y < self.height), "ERROR - Invalid coordinate set in is_set()"
336        bit = (self.buffer[x] >> y) & 1
337        return True if bit > 0 else False
338
339    def draw(self):
340        """
341        Takes the contents of _buffer and writes it to the LED matrix.
342        NOTE Overrides the parent method.
343        """
344        if self.is_rotated:
345            new_buffer = self._rotate_matrix(self.buffer, self.rotation_angle)
346        else:
347            new_buffer = bytearray(len(self.buffer))
348            for i in range(8): new_buffer[i] = self.buffer[i]
349        draw_buffer = bytearray(17)
350        for i in range(len(new_buffer)):
351            draw_buffer[i * 2 + 1] = (new_buffer[i] >> 1) | ((new_buffer[i] << 7) & 0xFF)
352        self.i2c.writeto(self.address, bytes(draw_buffer))
353
354    # ********** PRIVATE METHODS **********
355
356    def _rotate_matrix(self, input_matrix, angle=0):
357        """
358        Rotate an 8-integer matrix through the specified angle in 90-degree increments:
359           0 = none, 1 = 90 clockwise, 2 = 180, 3 = 90 anti-clockwise
360        """
361        assert angle in (0, 1, 2, 3), "ERROR - Invalid angle in _rotate_matrix()"
362        if angle is 0: return input_matrix
363
364        a = 0
365        line_value = 0
366        output_matrix = bytearray(self.width)
367
368        # NOTE It's quicker to have three case-specific
369        #      code blocks than a single, generic block
370        for y in range(self.height):
371            line_value = input_matrix[y]
372            for x in range(7, -1, -1):
373                a = line_value & (1 << x)
374                if a is not 0:
375                    if angle is 1:
376                        output_matrix[7 - x] = output_matrix[7 - x] + (1 << y)
377                    elif angle is 2:
378                        output_matrix[7 - y] += (1 << (7 - x))
379                    else:
380                        output_matrix[x] = output_matrix[x] + (1 << (7 - y))
381        return output_matrix
382
383    def _fill(value=0xFF):
384        """
385        Fill the buffer, column by column with the specified byte value
386        """
387        value &= 0xFF
388        for i in range(self.width): self.buffer[i] = value

References And Credits

  1. Tony Smith: https://github.com/smittytone/HT16K33-Python

  2. Gorillacell: gorillacell.kr



Posts in this series



No comments yet!

GitHub-flavored Markdown & a sane subset of HTML is supported.