Rothschopf Werner

rowex ganz privat


A HT16K33 Display Library for easy printing to LED Modules

The Holtek HT16K33 is a LED Display driver IC and can be used with I2C. Just two I2C wires enables you to control lot of LEDs . As long as you have I2C available - you can easily add this display driver to your Arduino project.

The Holtek HT16K33 LED Driver Chip

The HT16K33 can control up to 16 x 8 LEDs. There are Maker-friendly modules available with 7segment LEDs, 14segment LEDs, dot matrix and even "blank" PCB als simple backpacks for your own project. As the HT16K33 communicates via I2C you only need two GPIOs. If 128 LEDs are not enough - up to 8 chips can be controlled by selecting the I2C address. Yes - up to 1024 LEDs or 64 alphanumeric digits (each digit with 16 LEDs) driven by just two Arduino GPIOs! The HT16K33 comes in various SMD variants, the largest one can choose one of 8 eight I2C adresses (0x70 - 0x77). Only have to short 3 pads like in a 3bit binary Code (1-2-4). For example to get adress 0x73 you have to connect the pads 1 and 2. As with every other I2C device an I2C Scanner will help you to find the right adress of your device (yes - I did it wrong on my first multi display also...).

The HT16K33 supports "brightness" and several frequencies for "blinking", but only for all digits of the IC, not individually per LED segment or digit.

If you do the comparison with a MAX7219: the LED driver chip HT16K33 can support twice the LEDs of a MAX7219, needs just the two I2C wires but you only can connect up to 8 ICs. The HT16K33 doesn't have a built in character set - you have to care about each character in your software.

Modifications of Noiasca HT16K33 Library - Why a New Library?

In the Arduino world we are constantly using the so called "print" class. You will use it for Serial output (Serial.print), printing to LCDs, OLEDs or even for the output of a webserver. I added this "print" method to my Noiasca HT16K33 library. The new library offers easier output with print (and some other helpers). The idea is simple: I'm using "print" for output on Serial and I want to use it the same manner with the LED displays.

If 8 digits are not enough, I want add more modules. But in my sketch I but don't want to care any more about the hardware. I want to handle my big display "as one" and print out text like i would do it on Serial.

display.print(F("even a long text expanding over up to 8 modules"));

So - if you need to print lot of text (or large numbers) on LED displays or displays with more than one IC this library could make your life easier.

What's behind these modifications

One capability of C++ is, that a class can derive properties and characteristics from another class. This is called Inheritance. Exactly this method was used: the new library inherits the print method. To be precise: I needed to implemet the "write" method. This method writes one digit on the display. We inherite the print class (which uses the write method) and now we are able to "print" integers, floats, C-strings (char arrays), (Arduino) Strings to the display like you are used to from other libraries.

As you might know from other libraries they offer some helper methods. The method


is such an example, it will set the cursor to a defined position.

7 segment vs 14 segment character set

As explained on my MAX7219 page, there are a lot of restrictions on 7 segment displays, and all this restrictions are still true for the HT16K33 library. This is a limitation of the LED Display itself, not the driver chip. Obviously 14 segment displays can show much better characters.

Currently I haven't found any 16 segment modules. Therefore a dedicated 16 segment support is still missing. But as soon as someone points me to affordable HT16K33 modules with 16 segment LEDs, the implementation should be straight forward.

Speaking about character sets, the Noiasca HT16K33 library supports printable characters 0d32 to 0d127 from the ANSI character table.

Points and Dots

Points and dots are technically a small challange. As printing to the display is done character by character we have to find a way how to "activate" the decimal point of a previous printed digit. This is simply done by storing the last printed character. Therefore we can "reprint" the previous digit with an activated dot.

This works quite well - up to the point if the previous character is a dot. This just breaks the print out. This means you can easily print a dot after another character - but not a dot after a dot.

So this is not allowed


instead print a blank before the next dot, like following:

. . .

This will be shown up correctly on the display - and if you carefully check your display it IS exactly what you want: 7 segments must not be enlighted (all off), but the decimal point does. So it makes absolutly sense to print blanks between the dots.

End of Line

If we come to the "last" digit of our deivce, we have to to decide what should happen with the next character. On default the library jumps to the "NEXT_DEVICE". If we have written the last postion on the last device, we wrap around to the first device.

The Linefeed and Carriage Return

In this library, linefeed (LF, 0d10, 0x0A, \n) and carriage return (CR, 0d13, 0x0C, \r) will have no special effect. Both characters are below 0d32 and therefore classified as not printable.


It's always recommended to look for alternatives. So Google for HT16K33 libraries and have a look on some alternatives.

Like for lot of other modules - Adafruit - is always a good starting point for an Arduino Library. My first HT16K33 HW module is an original Adafruit 0.54" backpack. Lot of ideas came from their library. Adafruit does an amazing job providing us with libraries, support them and buy hardware from them!

Another alternative comes from Arduino forum user HKJ-lygte. He provides a generic LED display driver "library" and the HT16K33 is just one out of many supported ICs. I really liked his idea to split the hardware definition of each LED segment from the character set / font table. But there are a lot of precompiler #defines and I prefer to use the Arduino "standard" I2C library "wire.h" for my projects. Nevertheless his homepage is really worth a visit because he has a very comprehensive collection of available LED displays.

RAM and Flash Usage

I did some comparisions with other libraries. The print class will cost some extra RAM and flash. Nevertheless it will help you saving A LOT more RAM and flash memory the more you will print to your display. In lot of sketches you will use the serial.print - so lot of code of the stream librarey needs already compiled to your sketchand the new HT16K33 library is just reusing this code also. If you do your comparisions - just don't give up after the first line. Try a more complex example with lot of text and see, how easy it becomes with the Noiasca HT16K33 Library.

The example "12 Sevenseg" is a port of the "Adafruit LED Backpack" sevenseg example. The codes should do (nearly) the same, but you see already some differences in flash and RAM resources:

  flash RAM
Adafruit 5934 Bytes 456 Bytes
noiasca 4540 Bytes 445 Bytes


Both compiled for Arduino UNO on 1.9.2019, today's figures could differ. Other sketches could bring up other results.

Library Examples

The library comes with lot of examples, here are some of them:

Example Purpose
01 Strandtest 7segment a quick start to check, if your wiring is correct
02 Strandtest 14segment a quick start to check, if your wiring is correct
11 Hello World 14segment lot of print variants, including sprintf and floats
12 sevenseg a port of an Adafruit example to compare memory usage
13 format number 7segment example how to print numbers right aligned without sprintf
20 Multidisplay demo of 3 different displays using 4 ICs in one sketch
21 Scrolltext scroll text over 16 digits (2 ICs)
23 clock 14segment ds3231 a simple clock example for the RTC DS3231

If you have any ideas or a specific usecase, just come up with the idea you want do get added!

Installation and usage of the Noiasca HT16K33 Library

Download the ZIP (at the end of this page) and unzip it to your libraries folder. It might be necessary that you need a restart of your Arduino IDE before you can use the library.

There is an additional example sketch called 11_HelloWorld_14segment. It shows all available methods and some special things you should know about the library.

a) Include the new library into your sketch with

 #include <NoiascaHt16k33.h>

b) the library files

In general, there is no need to change anything in the .h/.cpp files of the library. All hardware relevant settings can be done in your sketch. Just use the proper constructer, the right parameters for the begin method or one of the setters for your display object. I don't like libraries when they force one to "manipulate" properties in any file. This is something I learned from Marco the author of the MD_MAX7219 library. Library files should be stable between all sketches they are included to. "Modifying" the library files might effect other sketches and therefore these modifications should be avoided.

There is only one exception to this rule: If you like to have debug messages from the library, this is can be activated - as in most libraries - in the .h header file.

c) see the examples

I've added some examples to make it easier to understand the functionality of the library. The first sketch should always be some "strandtest" to find out if your hardware works with the new software.

Important Methods:

Here are some of the important methods:

void begin(uint8_t i2c_addr, uint8_t numDevices = 1);

Defines the (start) I2C adress of your display and the number of used devices. If your display consists of more than one device, the other adresses must be ascending. (e.g. 0x70 0x71 0x72). If you only use one device, you can omit the optional numDevice parameter as it gets defaulted to 1 anyway if not set.

void blinkRate(uint8_t b);

Takes the constants HT16K33_BLINK_OFF, HT16K33_BLINK_2HZ, HT16K33_BLINK_1HZ or HT16K33_BLINK_HALFHZ and sets the blink rate of the display. As limited by the hardware all digits of your display will blink.

void clear(void);

clears the display (and the displaybuffer if used in future)

void setBrightness(uint8_t b);

sets the brightness of the display, takes a value from 0 lowest to 15 brightest.

void setCursor(uint8_t newPosition);

sets the cursor for the next writing operation to the definied new position

void setDigits(uint8_t newDigits);

sets number of digits per device. For usual the begin method sets the digits based on the used constructor. If you want to use some custom built displays you can limit the amout of used digits to your needs.

void writeLowLevel(uint8_t position, uint16_t bitmask);

sends the provided bitmask directly to the given position.

Supported Hardware

currently the library supports following hardware modules

Features Constructor
7 segment, 8 digit Noiasca_ht16k33_hw_7
7 segment, 4 digit, colon digit Noiasca_ht16k33_hw_7_4_c
14 segment, 8 digit Noiasca_ht16k33_hw_14
14 segment, 4 digit Noiasca_ht16k33_hw_14_4

Other modules might work also.


So, are there any reasons not to use Noiasca HT16K33? Yes, the library will have no dedicated support for dot-matrix displays. For dot-Matrix I still use the MAX7219 and I have no plan to migrate to the HT16K33. Also writing of individual segments is not in the focus of Noiasca HT16K33, even if you can write a raw bitmask to a digit.

Migration from another Library to Noiasca HT16K33

There is no easy way to switch from another library to Noiasca HT16K33. You have to check each "output". But the step is pretty easy: treat your display just like you would print to the serial monitor.

If you want to format numbers, use either sprintf or do it manually. You will find some ideas in the examples.



First upload: 2019-10-15 | Version: 2019-10-15