I recently built this toy for my nephew. I had absolutely no idea what to call it, but my wife assures me it is an Electronic Busy Board. So, if this page gets absolutely no views then I know that either she’s wrong, or my content is dreadful.
Here it is in action:
Part of the fun for me was designing it and writing all the code. Although it doesn’t pre-date AI, it certainly pre-dates me using AI. I’m an Electronic Engineer and I certainly wouldn’t use AI for designing the schematic or laying out the PCB (yet…), but if I were to embark on this project today, in October 2026, I’d certainly use AI to give me a leg-up with the firmware.
The Hardware
It took a good few weeks of thinking and tweaking in order to come up with the design. I am familiar with PIC microcontrollers, so I decided I wanted to stick with them. I just needed to find one with a large enough pin-count for everything I wanted to do.
I decided I wanted a few LED bar-graphs that could be changed by twiddling a knob, plus I liked the idea of a 7-segment display, and a rotary knob to select LEDs.
Luckily (or not so luckily if you’re having to write the code for this thing) the LED bar-graphs and 7-segment display I found were multiplexed. This means that the number of pins needed to control them was massively reduced from what you might expect, but the coding becomes a lot more complex. You can get ICs which will handle all of this for you, then present you with an I2C or SPI bus, like the MAX6954, but they are incredibly expensive (over $20!!) and I knew what I had to do to control it in software, so I just decided to do it all myself.
To put it simply, each segment of each of the four 7-segment digits (the anodes) is wired to the same segment of the other three digits. Then all of the cathodes for each segment are connected together and wired out one per digit. The downside with this is that you can only ever illuminate one of the four digits at a time, otherwise the digits would all light up the same. So, what you have to do is rapidly cycle through the digits one by one, quicker than the human eye can see, so it will appear to you that they are all driven completely independently and all illuminating at the same time.
Every single 7-segment clock or other display that you see in the world works like this – it’s just too many connections to wire up to have full individual control over each segment. Plus the current draw is massively reduced when multiplexing – you are only ever illuminating one digit at a time, rather than four.
The multi-coloured bar-graph was also multiplexed as three segments “digits” per bar-graph.
I found a huge PIC, the PIC18F67K40, which gave me 60 GPIO pins in a 64-pin package. Pretty good going!
There is no voltage regulator – I decided to just use the raw voltage from three AAA batteries. Finding a regulator that would work with the current I need, be easily available for me to buy and not waste loads of power in the “off” state proved very tricky. Then I remembered, why even bother with one! I bought the variant of the PIC that supports up to 5.5V fine.
I ended up using every single GPIO pin, as you can see in the schematic below. I didn’t think I’d be putting the schematic on the internet so it isn’t the neatest thing in the world, but it’s quite understandable.
This PCB was easily the most complex I’ve had to lay out at home. I was desperate to stick to just 2-layers for cost reasons, plus I am using Autodesk Fusion (used to be Eagle) which only gives me 2 layers for free. The section around the PIC was particularly difficult:

As is pretty conventional, the flood is GND, with vias spread around to connect all the GNDs up. I tried to route top to bottom on one layer and left to right on the second layer, but naturally in some places this wasn’t possible. I was pretty impressed at actually managing to route it all out on just the two layers though!
I used to always use oshpark for my PCBs, but for this one it was a lot cheaper for me to get it from PCBWay, given the size of the board. I was impressed with their website and the complexity of boards that they can manufacture. Oshpark still wins for the tiny little designs I do, but as the boards grow, PCBWay becomes the cheaper option.
The Software
I decided to control absolutely everything about this PCB with hardware timers. Long main loops where you try and get everything done before looping back around again are incredibly unwieldy and you end up with horrible hacks like “only do this bit every other loop” all over the place.
So, my main ended up looking like this:
while(1)
{
}
I set up 6 different timers, each causing an interrupt at a different rate, in which I could deal with whatever needed to be dealt with at that interval. Each of the push-buttons also caused an interrupt.
| Timer | Trigger Interval | What it does |
|---|---|---|
| TMR0 | 100 us | Controls perimeter and rotary-encoder ring LED brightness using software PWM. The brightness of each LED on the board can be set individually. This is to try to balance out mega-bright green LEDs with dimmer purple LEDs, etc. I could do it with different resistor values, but did it in software instead. Each LED is set to be “on” for a percentage of a 3 ms window (i.e. 30 cycles of TMR0). This timer turns each LED on or off, depending on if its allotted time has elapsed or not. |
| TMR2 | 1 ms | Multiplexes both LED bar-graphs and the four-digit stopwatch display. Simply cycles round the multiplexed LEDs constantly. |
| TMR1 | 10 ms | Reads both potentiometer ADC inputs and calculates their bar-graph levels. |
| TMR3 | 100 ms | Advances the running stopwatch by one tenth of a second; also supplies the clock for TMR7. |
| TMR4 | 1 second | Checks the power button; two consecutive pressed readings toggle power. This timer runs from a lower power clock, so it can remain enabled when the device is powered off. I had to poll the button in this manner because I accidentally forgot to connect it to an interrupt pin – oops! |
| TMR7 | 10 minutes | This is driven from TMR3 in order to get the timeout to be as long as 10 minutes. When this timer triggers the device “turns off” (enters the super low power state). Button presses and encoder movements reset this timer. |
As you can see from TMR4, the device is never truly “off” – it simply enters a super low-power state with the main oscillator disabled. It consumes only 374 nA in this state! It is powered from three AAA batteries and will last 305 years (!) in the low power state. I wanted to control the power like this rather than a traditional power switch so that I could automatically enter the “off” state after 10 minutes of inactivity. Otherwise a child would just leave the device turned on and the batteries would run flat in just a day.
The push-buttons were also mapped to interrupt routines. When a button is pressed, a variable is incremented to change the LED being illuminated (for example), which is then picked up by the relevant timer routine when it next runs. There was a bit of a quirk however, in that I wanted to debounce the button, otherwise a single push is registered as several pushes. Again, I’d rather do this in software than hardware – less soldering, and much easier to modify!
In order to debounce the switch I simply initiated a 50ms delay when the ‘press’ is detected, and if it is still ‘pressed’ 50ms later then that is a true press, otherwise it is ignored. This does have the annoyance that there is a 50ms delay in an interrupt routine, which isn’t idea, but the PIC has an easy way around this. I set all the timer interrupts to HIGH priority, and the switch interrupts to LOW priority. That way, the timer interrupts keep firing whilst we’re in the ‘button press’ interrupt routine – perfect! There are of course other ways to achieve this in code, but I’ve been given interrupt priorities, so I may as well use them.

The rotary encoder is a curious beast.
Ignoring the ‘push’ functionality, there are just three connections – A, B, and C.
The datasheet describes how they work:

C is connected to ground, then A and B behave as per the diagram when you turn it clockwise (CW) or counter-clockwise (CCW). D (presumably) stands for ‘detent’, which is the state of the lines when the encoder isn’t being turned. So, basically we need to watch the signals on those A and B lines and work out which way we are being turned, and how many times it has been turned.
I tried to code this all up myself from scratch, using interrupts firing on each edge, but it was never 100%, always occasionally missing a state.
After messing about with that I eventually found some absolutely rock-solid code online, reproduced here for anybody else to copy! The ISRs must be set to fire on the falling-edge only.
static volatile uint8_t ccw_fall = 0;
static volatile uint8_t cw_fall = 0;
static void encoder_a_isr(void)
{
if ((!cw_fall) && (ENCODER_A_GetValue() == 0) && (ENCODER_B_GetValue() == 1))
{
cw_fall = 1;
}
if ((ccw_fall) && (ENCODER_A_GetValue() == 0) && (ENCODER_B_GetValue() == 0))
{
cw_fall = 0;
ccw_fall = 0;
rotary_pos--;
TMR7_Reload(); // reload the 10 minute timer
if (rotary_pos < 0) rotary_pos = 7;
else if (rotary_pos > 7) rotary_pos = 0;
}
}
static void encoder_b_isr(void)
{
if ((!ccw_fall) && (ENCODER_A_GetValue() == 1) && (ENCODER_B_GetValue() == 0))
{
ccw_fall = 1;
}
if ((cw_fall) && (ENCODER_A_GetValue() == 0) && (ENCODER_B_GetValue() == 0))
{
cw_fall = 0;
ccw_fall = 0;
rotary_pos++;
TMR7_Reload(); // reload the 10 minute timer
if (rotary_pos < 0) rotary_pos = 7;
else if (rotary_pos > 7) rotary_pos = 0;
}
}
I know I’m quite verbose with the code – I could just do rotary_pos = (rotary_pos + 1) % 8; for the advancing, but I prefer to read it in a verbose way so that’s how I write it!
Is it safe for children?
Well, no, probably not. Clearly I’ve not put this through any kind of safety testing, as it’s a toy for my family. I’d never sell it.
In order to address the most obvious safety concerns, I covered the back in sticky foam to cover all the sharp solder joints, and cable-tied the batteries to the board so a child can’t remove them.

I think that’s just about everything… I may update this as I think of more, but I think that just about covers it!
It was an enjoyable experience designing, procuring components in an inexpensive way, and writing the firmware for this device.
Anyone that knows anything about children will know that the more effort you put into something, the less they’ll actually use (or eat…) it, but even if it does get chucked into the corner, at least I had fun making it!
