HT16K33 8x8 Dot-Matrix Display

The Keyestudio I2C 8x8 Dot-Matrix display [1]. is a clear, bright display for representing data, such as analogue values, temperatures, or for playing games.

HT16K338x8.jpg 

It's based on an HT16K33 I2C driver chip [2]. This code will also work with the similar display from Adafruit [3].

Three solder links allow you to choose one of eight I2C addresses for each display, allowing you to drive up to eight displays simultaneously. The default I2C address with no links is #x70 or 112.

Initialising the display

The variable adr is used to specify the I2C address of the display; by default this is 112:

(defvar adr 112)

The routine set initialises the display and sets the brightness, from 0 (off) to 15 (maximum):

(defun set (bri)
  (with-i2c (s adr)
    (write-byte #x21 s)
    (restart-i2c s)
    (write-byte #x81 s)
    (restart-i2c s)
    (write-byte (+ #xe0 bri) s)))

The routine clr clears the display by setting each column to zero:

(defun clr ()
  (with-i2c (s adr)
    (dotimes (x 16)
      (write-byte #x00 s))))

Displaying a bitmap

The following routine put displays an 8x8 bitmap on the display:

(defun put (&rest byt)
  (with-i2c (s adr)
    (write-byte 0 s)
    (dotimes (n 8)
      (let ((col (nth n byt)))
        (write-byte (logior (ash col -1) (ash col 7)) s)
        (write-byte 0 s)))))

The parameter is a list of eight bytes representing the columns you want to display.

For example, the following call displays a smiley (rotated through 90°). Using binary numbers for the columns makes it easy to design the bitmap:

(put #b00111100
     #b01000010
     #b10100101
     #b10000001
     #b10100101
     #b10011001
     #b01000010
     #b00111100)

The expression:

(logior (ash col -1) (ash col 7))

is designed to compensate for the fact that for some reason the rows are numbered 7, 0, 1, 2, 3, 4, 5, 6 from bottom to top.

Plotting a point

The plt routine treats the display as an 8x8 graphics display, and allows you to toggle one LED on or off by specifying its coordinates:

(defun plt (x y)
  (let (b)
    (with-i2c (s adr)
      (write-byte (* x 2) s)
      (restart-i2c s 1)
      (setq b (read-byte s))
      (restart-i2c s)
      (write-byte (* x 2) s)
      (write-byte
       (logxor b (ash 1 (logand (+ y 7) 7))) s))))

For example, to toggle the LED in the bottom left-hand corner (with the connector at the bottom), give the command:

(plt 0 0)

Again, the expression:

(logand (+ y 7) 7)

compensates for the odd row numbering.

For a simple demo that randomly toggles LEDs on the display to give a changing series of patterns see LED 8x8 dot-matrix display.


  1. ^ Keyestudio I2C 8x8 LED Matrix on AliExpress.
  2. ^ HT16K33 Datasheet on Adafruit.
  3. ^ Mini 8x8 LED Matrix w/I2C Backpack on Adafruit.