Custom 7 segment Display - 3 digits and custom indicators
This PCB is a 7 segment display LED array, made of 32 0603 LEDs, on which you can mount a 3d printed diffuser that shapes the LED segments. The idea is that from one PCB with 32 LEDS, some of which are arranged in 3 digits 7 segment displays, you can design your own custom diffuser, to build custom 7 segment displays, with custom LED indicators. The display is driven by 2 AW9523 ICs which are extremely cheap (30 cents each) and easy to control via I²C.
Created by
Clém
Tier 4
6 views
0 followers
m0.hid ⚡
submitted Custom 7 segment Display - 3 digits and custom indicators for review ago
Clém
submitted Custom 7 segment Display - 3 digits and custom indicators for review ago
m0.hid ⚡
requested changes for Custom 7 segment Display - 3 digits and custom indicators ago
Hey! Great work on this, but can you add your demo link to your blueprint submission? I don't see it here currently. Once you do that we can review it again!
Tier: 4
Tanuki ⚡🚀
submitted Custom 7 segment Display - 3 digits and custom indicators for review ago
Clém
submitted Custom 7 segment Display - 3 digits and custom indicators for review ago
Clém
added to the journal ago
Corrected the firmware
I wasn't satisfied with the firmware, because even though it was working, the way I drove the segment wasn't efficient, because the MCU sent the value for each 32 LED, even if the LED state didn't changed. and updating a frame was too slow, resulting in short black frames in between each number digits update.
So I tried to take a different approach, by creating 6 arrays, 3 for the IC A, and 3 for the IC B.
One array links the segments to the actual pin, and the two other boolean arrays describe the state of each LED. (16 LED per IC, so 16 values in the array, and for convenience I created a 4 by 4 array) One for the actual state, one for the new state. Every function that changes the display applies it's modification by changing specific bits in the 'New state' array, and then it calls the refreshDisplay() function that only send the display segments that changed, by comparing the 'previous state' to the 'New state' (and once it's done, it updates the 'previous state' by setting all bits to the corresponding ones in the 'New state' array)
So I had to changed every function, which took me quite a while, and I had to debug a few malfunctionning things before getting something reliable, but I think this firmware V2 is better than the previous one.
Actually, it's still 10kB, so 30% of the atmega328p flash, so it's definitely not the most efficient programm, but I guess if we want smth even more 'straight to the point' and 'compact' code, there is still room for improvement. In any case, this is way enough for my use cases (usually, memory space isn't the bottleneck in my projects), so I'm 100% satisfied with this code.
The new demo is available on the the Github repo.

A 'brief' explanation:
brightness is a global variable that set the brightness of the display.
Each segment can be independently changed by writting in the NstateMatrixA[][] or NstateMatrixB[][] array:
(refer to the array map). For example:
NstateMatrixA[0][2] = 1; //segment E 3 on
NstateMatrixB[1][1] = 0; //segment F 1 off
Notice:
Once the arrays are updated, for the display to actually show changes, we have to call the function refreshDisplay() orforcedRefreshDisplay():
There are 8 functions:
displayDIGIT (int number, int digit)
displays the number from 0 to 9 in the 'digit' position (1, 2 or 3). *RD
clearNumbers()
when called, the 3 digits area is turned off. *RD
clearALL()
All LEDs are turned off. *RD
refreshDisplay()
shows the changes on the display. Only send the segments that changes, with the segments being lit with the brightness brightness
forcedRefreshDisplay()
shows the changes on the display. All segments are sent, which can be useful for example if we gradually fade the LEDs, incrementing the brightness at each step: as the LED state don't change (only the brightness), the refreshDisplay() won't update the brightness.
The brightness variable as to be incremented globally.
litALL()
All the LEDs are lit with the brightness brightness. *RD
displayNumber (int value)
takes in input a number from 0 to 999, and displays it with the brightness brightness. *RD
LightBar (int value)
lit 1, 2, 3, 4, or 5 LEDs of the light bar depending on the value in parameter.
*RD: already comes with the refreshDisplay() function built-in
Clém
added to the journal ago
Built the display...
I uploaded the test demo code on an Arduino Mega 2560, and was after swapping the I2C addresses, I was amazed to see that it worked almost perfectly: somehow, two LEDs pin were flipped in the code, so a different LED would lit up when trying to turn on the segment D, digit 2. So I just had to swap the two pin assigments on the code. Other than that, everything looked good.


Top cover:
I then loaded up the 3d printed top cover on Cura, sliced it with my favorite feature , the fuzzy skin, and printed the parts on my ender 3 in black filament, so that the light don't passes through and the segments are sharp and well separated. The top part turned out really well, even though I still had to sand both sides because the top and bottom layers weren't flawless, but for the 4 tiles, things were a bit different: I think the holes weren't thick enough in CAD, because when printing, it almost fused from all sides, and the logos were almost invisible (CG, OUT, IN and power logo didn't turned great). Not a big deal, maybe a V2 top cover will fix these problems.


I added 4 threaded inserts and screwed the 3d printed part on top of the PCB. Whithout waiting further, I placed a sheet of tracing paper on top to see how it would look like, and it was stunning: the result definitely exceeded my expectations. The segments were sharp and bright (it's not even max brighness on the sofware side!) and the orange LEDs looked so cool with the grayish frame.
So I took a small rectangle of double sided adhesive, and securely fixed the tracing paper on top. I then trimmed the edge, and... Here is the result:






Clém
added to the journal ago
Received the PCBs and soldered the board

I soldered the PCB, and started adding all the 0603 LEDs, making sure they where in the right orientation, that I then reflow soldered with hot air from the back of the PCB. I then populated the other side, with the ICs, Caps and resistors and FPC. I had to blow air from above the ICs and other components, because otherwise the LEDs soldered on the other side would have been blown away, so I was a bit scared, but eventually it turned out really well. Lastly, I soldered the Header pins, and the PCB was good to go!


Clém
added to the journal ago
Ordered the PCB
I've ordered the PCB only today, january 1st GMT+8 because the $1.70 Global standard Direct Line shipping offer is only applicable 3 times a month ($9 instead 😮), and I noticed that as Blueprint boosted my creativity, I've already placed 3 JLC PCB orders in december 😅

Clém
added to the journal ago
Coded the Arduino demo code.

It took me quite a while, and it was a quite repetive task, because I needed to assign each hardware pin the related software number (from 0 to 15, decided by the AW9523 library). I was alternating between the schematic, the PCB layout, Adafruit AW9523 board layout and documentation... I think I've got something that should work, and I've then coded a demo, testing the 3-digit segmented display, 5-LEDs Light Bar...
I've made a few functions, so it's easier to try things, but it's far from the most optimized code: it's just to see if it works or not.
The functions are the following:
clearALL() Clear all the displays (all LEDs turned off)
clearNumbers() Clear only the 3 digits
displayDIGIT (int number, int digit) Write the number number at the 1st, 2nd or 3rd digit (digit)
LightBar (int value) set the numbers of LEDs lit on the Light Bar.
With this demo, we should get the 2 dots fading in, the icons lighting up, the light bar level rising and the display counting up to 999.
Clém
added to the journal ago
Designed one top cover diffuser (3D printed part)
I designed one top cover (as we can make as many differents designs as we want to), with the digit font inspired from Seamless (Posy, https://www.youtube.com/watch?v=RTB5XhjbgZA&t=301s 4:35).
There are 4 tiles that could be swapped with whatever design you want, and a 5-LEDs light bar on the bottom left corner, which could be used for status indication, volume, level, ...)
We can securely attach the top cover (3d printed part) to the PCB with 4 M2 phillips head screws. I'll add 4 threaded inserts into the 3d printed part.
For the diffusing layer, I'm planning to add a double sided adhesive sheet with tracing paper on top: trust me, it gives pretty good results!😉
Here is how it looks like:







Clém
added to the journal ago
Made some cool renders of the PCB
As always, I've loaded up the 3d file to Fusion 360, added some appearances, and started doing a few renders. I even added an FPC ribbon cable which I think looks pretty cool.






Clém
added to the journal ago
Finished the PCB
I cleaned the silkscreen of each component, and added my own designs and to be honest, I'm really happy with the result.
Here is how the final PCB looks like:






Clém
added to the journal ago
Routed the PCB - Part 2

Ok, so, I continued routing the LEDs to the ICs, but I don't know how I didn't notice it before: I placed both AW9523 just behind an LED... Which might not be the best idea regarding thermal management, as both the IC and the LED heat up a bit, and considering the fact that there is plenty of empty space between the LEDs, which are better places for the IC to be. So, I'm going to offset them a bit by a few millimeters, and it should no longer be a design flaw. And while we're at it, I made a cutout in the Top Solder Mask layer under each IC, so that the copper heatsink is exposed.
Before:

Finishing the routing
I then finished routing all the LEDs and other pins, and added a few power planes and vias (VCC, VCC.LED, GND), with a few prohibited regions so it's neat and looks better. Thankfully routing all of this on a 2 2 layer board ended up being easier than I initially thought.
So here is the PCB with the ICs shifted a bit (After) :

And here are some screenshots of the PCB:




Overall, I'm pretty happy of how the PCB turned out. Now, as always, the last thing to do for the PCB design is to clean the silkscreen layer...
Clém
added to the journal ago
Routed the PCB - Part 1
I arranged all the components on a 25x80mm PCB. All the LEDs on one side, all the others components on the other side. I started routing every LEDs to the closest IC pin, and changed the schematic to match the routing.

Clém
added to the journal ago
Made the schematic
I've already worked with these hidden IC gems: the AWINIC AW9523. This IC is a 16 pin GPIO expander, with a dedicated constant current driver for each GPIO, so it can drive 16 LEDs with 256 steps, without any series resistor, nor worrying about flickering. It can be controlled with an MCU via I²C, and you can have up to 4 of these chips on one I²C bus. Adafruit has made a great library for them in the Arduino IDE, and the best part, it only costs around 30 cents each! (I bought 20 of them on Aliexpress for $5.70! 😮😁). I had to use them here.
Okay, fine, maybe this chip isn't perfect, because when you change the I²C address, you also change the initialization settings, so at boot up, every GPIO is in sink mode (pulled LOW), causing all the LEDs to light up a bit too brightly for a brief moment, so it can stress the LEDs, and obviously it causes a nasty current spike... But you can easily bypass this weird behavior with a high-side switch P-channel Mosfet that enables VCC.LED only after the AW9523 has been properly initialized. At least it's how I think it'll work...
So the schematic consists of 2 AW9523 ICs, 32 LEDs (0603 LEDs, I'll choose red ones for their low forward voltage if my projects runs at 3.3V, but white ones otherwise (5V) because I think it'll be cooler), optional 4.7k pull-up on SDA and SCL (if needed), a header for connecting the module along with a FPC 6P connector. It'll depend whether I use Duponds headers or FPC for my projects.
Here is what it looks like:

We could use up to 2 of these Segmented displays modules per I²C bus. (4 ICs maximum on the same I²C bus), which I think is enough for most projects.
Clém
added to the journal ago
How I got the idea...
While dismantling an electronic dishwasher, I salvaged the HMI electronic board, and I wanted to unsolder the 7 segment display to reuse it in future projects, but I discovered that it wasn't a 7 segment display module, but only 0603 white LEDs arranged on the PCB, with a white and black molded plastic diffuser above it. I realize that it could be really fun to build custom seven segment displays, with custom icons, or even custom fonts for the digits! Same thing here: Arranging the LEDs on a PCB, and making a 3d printed diffuser with the custom design. It would be pretty cool, and much funnier compared to boring classical seven segment displays! A while ago, I stumbled upon a cool Youtube video from Pozy, Segmented Displays (link: https://www.youtube.com/watch?v=RTB5XhjbgZA&t=301s) innovating with new segmented displays designs, and in my opinion, the Seamless design (4:35) was pretty cool.

So I wanted to try to create a 7 segment display module, with 3 digits made with this font, and with custom icons on the side. The cool thing with this design, is that with one PCB, we can add different top 3d printed diffusers, to get different results for multiple applications.
Clém
started Custom 7 segment Display - 3 digits and custom indicators ago
12/25/2025 8 PM - How I got the idea...
While dismantling an electronic dishwasher, I salvaged the HMI electronic board, and I wanted to unsolder the 7 segment display to reuse it in future projects, but I discovered that it wasn't a 7 segment display module, but only 0603 white LEDs arranged on the PCB, with a white and black molded plastic diffuser above it. I realize that it could be really fun to build custom seven segment displays, with custom icons, or even custom fonts for the digits! Same thing here: Arranging the LEDs on a PCB, and making a 3d printed diffuser with the custom design. It would be pretty cool, and much funnier compared to boring classical seven segment displays! A while ago, I stumbled upon a cool Youtube video from Pozy, Segmented Displays (link: https://www.youtube.com/watch?v=RTB5XhjbgZA&t=301s) innovating with new segmented displays designs, and in my opinion, the Seamless design (4:35) was pretty cool.

So I wanted to try to create a 7 segment display module, with 3 digits made with this font, and with custom icons on the side. The cool thing with this design, is that with one PCB, we can add different top 3d printed diffusers, to get different results for multiple applications.
12/25/2025 10 PM - Made the schematic
I've already worked with these hidden IC gems: the AWINIC AW9523. This IC is a 16 pin GPIO expander, with a dedicated constant current driver for each GPIO, so it can drive 16 LEDs with 256 steps, without any series resistor, nor worrying about flickering. It can be controlled with an MCU via I²C, and you can have up to 4 of these chips on one I²C bus. Adafruit has made a great library for them in the Arduino IDE, and the best part, it only costs around 30 cents each! (I bought 20 of them on Aliexpress for $5.70! 😮😁). I had to use them here.
Okay, fine, maybe this chip isn't perfect, because when you change the I²C address, you also change the initialization settings, so at boot up, every GPIO is in sink mode (pulled LOW), causing all the LEDs to light up a bit too brightly for a brief moment, so it can stress the LEDs, and obviously it causes a nasty current spike... But you can easily bypass this weird behavior with a high-side switch P-channel Mosfet that enables VCC.LED only after the AW9523 has been properly initialized. At least it's how I think it'll work...
So the schematic consists of 2 AW9523 ICs, 32 LEDs (0603 LEDs, I'll choose red ones for their low forward voltage if my projects runs at 3.3V, but white ones otherwise (5V) because I think it'll be cooler), optional 4.7k pull-up on SDA and SCL (if needed), a header for connecting the module along with a FPC 6P connector. It'll depend whether I use Duponds headers or FPC for my projects.
Here is what it looks like:

We could use up to 2 of these Segmented displays modules per I²C bus. (4 ICs maximum on the same I²C bus), which I think is enough for most projects.
12/26/2025 11 AM - Routed the PCB - Part 1
I arranged all the components on a 25x80mm PCB. All the LEDs on one side, all the others components on the other side. I started routing every LEDs to the closest IC pin, and changed the schematic to match the routing.

12/26/2025 5 PM - Routed the PCB - Part 2

Ok, so, I continued routing the LEDs to the ICs, but I don't know how I didn't notice it before: I placed both AW9523 just behind an LED... Which might not be the best idea regarding thermal management, as both the IC and the LED heat up a bit, and considering the fact that there is plenty of empty space between the LEDs, which are better places for the IC to be. So, I'm going to offset them a bit by a few millimeters, and it should no longer be a design flaw. And while we're at it, I made a cutout in the Top Solder Mask layer under each IC, so that the copper heatsink is exposed.
Before:

Finishing the routing
I then finished routing all the LEDs and other pins, and added a few power planes and vias (VCC, VCC.LED, GND), with a few prohibited regions so it's neat and looks better. Thankfully routing all of this on a 2 2 layer board ended up being easier than I initially thought.
So here is the PCB with the ICs shifted a bit (After) :

And here are some screenshots of the PCB:




Overall, I'm pretty happy of how the PCB turned out. Now, as always, the last thing to do for the PCB design is to clean the silkscreen layer...
12/26/2025 10 PM - Finished the PCB
I cleaned the silkscreen of each component, and added my own designs and to be honest, I'm really happy with the result.
Here is how the final PCB looks like:






12/27/2025 11 AM - Made some cool renders of the PCB
As always, I've loaded up the 3d file to Fusion 360, added some appearances, and started doing a few renders. I even added an FPC ribbon cable which I think looks pretty cool.






12/27/2025 4 PM - Designed one top cover diffuser (3D printed part)
I designed one top cover (as we can make as many differents designs as we want to), with the digit font inspired from Seamless (Posy, https://www.youtube.com/watch?v=RTB5XhjbgZA&t=301s 4:35).
There are 4 tiles that could be swapped with whatever design you want, and a 5-LEDs light bar on the bottom left corner, which could be used for status indication, volume, level, ...)
We can securely attach the top cover (3d printed part) to the PCB with 4 M2 phillips head screws. I'll add 4 threaded inserts into the 3d printed part.
For the diffusing layer, I'm planning to add a double sided adhesive sheet with tracing paper on top: trust me, it gives pretty good results!😉
Here is how it looks like:







12/29/2025 - Coded the Arduino demo code.

It took me quite a while, and it was a quite repetive task, because I needed to assign each hardware pin the related software number (from 0 to 15, decided by the AW9523 library). I was alternating between the schematic, the PCB layout, Adafruit AW9523 board layout and documentation... I think I've got something that should work, and I've then coded a demo, testing the 3-digit segmented display, 5-LEDs Light Bar...
I've made a few functions, so it's easier to try things, but it's far from the most optimized code: it's just to see if it works or not.
The functions are the following:
clearALL() Clear all the displays (all LEDs turned off)
clearNumbers() Clear only the 3 digits
displayDIGIT (int number, int digit) Write the number number at the 1st, 2nd or 3rd digit (digit)
LightBar (int value) set the numbers of LEDs lit on the Light Bar.
With this demo, we should get the 2 dots fading in, the icons lighting up, the light bar level rising and the display counting up to 999.
CustomSegmentedDisplayMINIMALARDUINOCodeAW9523
12/31/2025 - Ordered the PCB
I've ordered the PCB only today, january 1st GMT+8 because the $1.70 Global standard Direct Line shipping offer is only applicable 3 times a month ($9 instead 😮), and I noticed that as Blueprint boosted my creativity, I've already placed 3 JLC PCB orders in december 😅

1/20/2026 - Received the PCBs and soldered the board

I soldered the PCB, and started adding all the 0603 LEDs, making sure they where in the right orientation, that I then reflow soldered with hot air from the back of the PCB. I then populated the other side, with the ICs, Caps and resistors and FPC. I had to blow air from above the ICs and other components, because otherwise the LEDs soldered on the other side would have been blown away, so I was a bit scared, but eventually it turned out really well. Lastly, I soldered the Header pins, and the PCB was good to go!

1/26/2026 - Built the display...
I uploaded the test demo code on an Arduino Mega 2560, and was after swapping the I2C addresses, I was amazed to see that it worked almost perfectly: somehow, two LEDs pin were flipped in the code, so a different LED would lit up when trying to turn on the segment D, digit 2. So I just had to swap the two pin assigments on the code. Other than that, everything looked good.


Top cover:
I then loaded up the 3d printed top cover on Cura, sliced it with my favorite feature , the fuzzy skin, and printed the parts on my ender 3 in black filament, so that the light don't passes through and the segments are sharp and well separated. The top part turned out really well, even though I still had to sand both sides because the top and bottom layers weren't flawless, but for the 4 tiles, things were a bit different: I think the holes weren't thick enough in CAD, because when printing, it almost fused from all sides, and the logos were almost invisible (CG, OUT, IN and power logo didn't turned great). Not a big deal, maybe a V2 top cover will fix these problems.


I added 4 threaded inserts and screwed the 3d printed part on top of the PCB. Whithout waiting further, I placed a sheet of tracing paper on top to see how it would look like, and it was stunning: the result definitely exceeded my expectations. The segments were sharp and bright (it's not even max brighness on the sofware side!) and the orange LEDs looked so cool with the grayish frame.
So I took a small rectangle of double sided adhesive, and securely fixed the tracing paper on top. I then trimmed the edge, and... Here is the result:






1/27/2026 - Corrected the firmware
I wasn't satisfied with the firmware, because even though it was working, the way I drove the segment wasn't efficient, because the MCU sent the value for each 32 LED, even if the LED state didn't changed. and updating a frame was too slow, resulting in short black frames in between each number digits update.
So I tried to take a different approach, by creating 6 arrays, 3 for the IC A, and 3 for the IC B.
One array links the segments to the actual pin, and the two other boolean arrays describe the state of each LED. (16 LED per IC, so 16 values in the array, and for convenience I created a 4 by 4 array) One for the actual state, one for the new state. Every function that changes the display applies it's modification by changing specific bits in the 'New state' array, and then it calls the refreshDisplay() function that only send the display segments that changed, by comparing the 'previous state' to the 'New state' (and once it's done, it updates the 'previous state' by setting all bits to the corresponding ones in the 'New state' array)
So I had to changed every function, which took me quite a while, and I had to debug a few malfunctionning things before getting something reliable, but I think this firmware V2 is better than the previous one.
Actually, it's still 10kB, so 30% of the atmega328p flash, so it's definitely not the most efficient programm, but I guess if we want smth even more 'straight to the point' and 'compact' code, there is still room for improvement. In any case, this is way enough for my use cases (usually, memory space isn't the bottleneck in my projects), so I'm 100% satisfied with this code.
The new demo is available on the the Github repo.

A 'brief' explanation:
brightness is a global variable that set the brightness of the display.
Each segment can be independently changed by writting in the NstateMatrixA[][] or NstateMatrixB[][] array:
(refer to the array map). For example:
NstateMatrixA[0][2] = 1; //segment E 3 on
NstateMatrixB[1][1] = 0; //segment F 1 off
Notice:
Once the arrays are updated, for the display to actually show changes, we have to call the function refreshDisplay() orforcedRefreshDisplay():
There are 8 functions:
displayDIGIT (int number, int digit)
displays the number from 0 to 9 in the 'digit' position (1, 2 or 3). *RD
clearNumbers()
when called, the 3 digits area is turned off. *RD
clearALL()
All LEDs are turned off. *RD
refreshDisplay()
shows the changes on the display. Only send the segments that changes, with the segments being lit with the brightness brightness
forcedRefreshDisplay()
shows the changes on the display. All segments are sent, which can be useful for example if we gradually fade the LEDs, incrementing the brightness at each step: as the LED state don't change (only the brightness), the refreshDisplay() won't update the brightness.
The brightness variable as to be incremented globally.
litALL()
All the LEDs are lit with the brightness brightness. *RD
displayNumber (int value)
takes in input a number from 0 to 999, and displays it with the brightness brightness. *RD
LightBar (int value)
lit 1, 2, 3, 4, or 5 LEDs of the light bar depending on the value in parameter.
*RD: already comes with the refreshDisplay() function built-in