Silicon ChipMax’s Cool Beans - November 2025 SILICON CHIP
  1. Contents
  2. Publisher's Letter: Many SSDs have a limited lifespan no matter what
  3. Project: Capacitor Discharger by Andrew Levido
  4. Feature: Teach-In 12.1 by Mike Tooley
  5. Project: 5MHz 40A Current Probe by Andrew Levido
  6. Feature: Max’s Cool Beans by Max the Magnificent
  7. Project: 3D Printer Filament Drying Chamber, Part 2 by Phil Prosser
  8. Feature: Techno Talk by Max the Magnificent
  9. Feature: Circuit Surgery by Ian Bell
  10. Feature: Net Work by Alan Winstanley
  11. Feature: Nikola Tesla, the original ‘mad scientist’, Part 2 by Tim Blythman
  12. Subscriptions
  13. Feature: Audio Out by Jake Rothman
  14. PartShop
  15. Advertising Index
  16. Market Centre
  17. Back Issues

This is only a preview of the November 2025 issue of Practical Electronics.

You can view 0 of the 80 pages in the full issue.

Articles in this series:
  • Max’s Cool Beans (January 2025)
  • Max’s Cool Beans (February 2025)
  • Max’s Cool Beans (March 2025)
  • Max’s Cool Beans (April 2025)
  • Max’s Cool Beans (May 2025)
  • Max’s Cool Beans (June 2025)
  • Max’s Cool Beans (July 2025)
  • Max’s Cool Beans (August 2025)
  • Max’s Cool Beans (September 2025)
  • Max’s Cool Beans: Weird & Wonderful Arduino Projects (October 2025)
  • Max’s Cool Beans (November 2025)
Items relevant to "3D Printer Filament Drying Chamber, Part 2":
  • Filament Dryer Control PCB [28110241] (AUD $7.50)
  • PIC16F15214-I/P programmed for the 3D Printer Filament Dryer [2811024A.HEX] (Programmed Microcontroller, AUD $10.00)
  • Firmware and 3D printing (STL) files for the 3D Printer Filament Dryer (Software, Free)
  • Filament Dryer Control PCB pattern (PDF download) [28110241] (Free)
  • 3D Printer Filament Dryer drilling templates (Panel Artwork, Free)
Articles in this series:
  • 3D Printer Filament Dryer, Part 1 (October 2024)
  • 3D Printer Filament Dryer, Part 2 (November 2024)
  • 3D Printer Filament Drying Chamber, Part 1 (October 2025)
  • 3D Printer Filament Drying Chamber, Part 2 (November 2025)
Articles in this series:
  • Techno Talk (February 2020)
  • Techno Talk (March 2020)
  • (April 2020)
  • Techno Talk (May 2020)
  • Techno Talk (June 2020)
  • Techno Talk (July 2020)
  • Techno Talk (August 2020)
  • Techno Talk (September 2020)
  • Techno Talk (October 2020)
  • (November 2020)
  • Techno Talk (December 2020)
  • Techno Talk (January 2021)
  • Techno Talk (February 2021)
  • Techno Talk (March 2021)
  • Techno Talk (April 2021)
  • Techno Talk (May 2021)
  • Techno Talk (June 2021)
  • Techno Talk (July 2021)
  • Techno Talk (August 2021)
  • Techno Talk (September 2021)
  • Techno Talk (October 2021)
  • Techno Talk (November 2021)
  • Techno Talk (December 2021)
  • Communing with nature (January 2022)
  • Should we be worried? (February 2022)
  • How resilient is your lifeline? (March 2022)
  • Go eco, get ethical! (April 2022)
  • From nano to bio (May 2022)
  • Positivity follows the gloom (June 2022)
  • Mixed menu (July 2022)
  • Time for a total rethink? (August 2022)
  • What’s in a name? (September 2022)
  • Forget leaves on the line! (October 2022)
  • Giant Boost for Batteries (December 2022)
  • Raudive Voices Revisited (January 2023)
  • A thousand words (February 2023)
  • It’s handover time (March 2023)
  • AI, Robots, Horticulture and Agriculture (April 2023)
  • Prophecy can be perplexing (May 2023)
  • Technology comes in different shapes and sizes (June 2023)
  • AI and robots – what could possibly go wrong? (July 2023)
  • How long until we’re all out of work? (August 2023)
  • We both have truths, are mine the same as yours? (September 2023)
  • Holy Spheres, Batman! (October 2023)
  • Where’s my pneumatic car? (November 2023)
  • Good grief! (December 2023)
  • Cheeky chiplets (January 2024)
  • Cheeky chiplets (February 2024)
  • The Wibbly-Wobbly World of Quantum (March 2024)
  • Techno Talk - Wait! What? Really? (April 2024)
  • Techno Talk - One step closer to a dystopian abyss? (May 2024)
  • Techno Talk - Program that! (June 2024)
  • Techno Talk (July 2024)
  • Techno Talk - That makes so much sense! (August 2024)
  • Techno Talk - I don’t want to be a Norbert... (September 2024)
  • Techno Talk - Sticking the landing (October 2024)
  • Techno Talk (November 2024)
  • Techno Talk (December 2024)
  • Techno Talk (January 2025)
  • Techno Talk (February 2025)
  • Techno Talk (March 2025)
  • Techno Talk (April 2025)
  • Techno Talk (May 2025)
  • Techno Talk (June 2025)
  • Techno Talk (July 2025)
  • Techno Talk (August 2025)
  • Techno Talk (October 2025)
  • Techno Talk (November 2025)
Articles in this series:
  • Circuit Surgery (April 2024)
  • STEWART OF READING (April 2024)
  • Circuit Surgery (May 2024)
  • Circuit Surgery (June 2024)
  • Circuit Surgery (July 2024)
  • Circuit Surgery (August 2024)
  • Circuit Surgery (September 2024)
  • Circuit Surgery (October 2024)
  • Circuit Surgery (November 2024)
  • Circuit Surgery (December 2024)
  • Circuit Surgery (January 2025)
  • Circuit Surgery (February 2025)
  • Circuit Surgery (March 2025)
  • Circuit Surgery (April 2025)
  • Circuit Surgery (May 2025)
  • Circuit Surgery (June 2025)
  • Circuit Surgery (July 2025)
  • Circuit Surgery (August 2025)
  • Circuit Surgery (September 2025)
  • Circuit Surgery (October 2025)
  • Circuit Surgery (November 2025)
Articles in this series:
  • Win a Microchip Explorer 8 Development Kit (April 2024)
  • Net Work (May 2024)
  • Net Work (June 2024)
  • Net Work (July 2024)
  • Net Work (August 2024)
  • Net Work (September 2024)
  • Net Work (October 2024)
  • Net Work (November 2024)
  • Net Work (December 2024)
  • Net Work (January 2025)
  • Net Work (February 2025)
  • Net Work (March 2025)
  • Net Work (April 2025)
  • Net Work (September 2025)
  • Net Work (November 2025)
Articles in this series:
  • The life of Nikola Tesla, Part 1 (October 2024)
  • Nikola Tesla, Part 2 (November 2024)
  • Nikola Tesla, the original ‘mad scientist’, Part 1 (October 2025)
  • Nikola Tesla, the original ‘mad scientist’, Part 2 (November 2025)
Articles in this series:
  • Audio Out (January 2024)
  • Audio Out (February 2024)
  • AUDIO OUT (April 2024)
  • Audio Out (May 2024)
  • Audio Out (June 2024)
  • Audio Out (July 2024)
  • Audio Out (August 2024)
  • Audio Out (September 2024)
  • Audio Out (October 2024)
  • Audio Out (March 2025)
  • Audio Out (April 2025)
  • Audio Out (May 2025)
  • Audio Out (June 2025)
  • Audio Out (July 2025)
  • Audio Out (August 2025)
  • Audio Out (September 2025)
  • Audio Out (October 2025)
  • Audio Out (November 2025)
Max’s Cool Beans By Max the Magnificent Weird & Wonderful Arduino Projects Part 11: connecting the console's 7-segment displays A fter all sorts of interesting discussions about boolean logic and such over the last few issues, it’s time for the other shoe to drop. I fear we’ve dillied and dallied in recent columns, meandering our way through a morass of teasingly tempting topics. You may well wonder if we’ve lost sight of our original goal, which is to construct a retro games console that will be the object of desire and the envy of all who see it (see Photo 1). “No!” I cry, “A thousand times no!” In this column, we will take the first tentative steps toward adding the 7-segment displays at the top of the console’s front panel (visible at upper left in Photo 1). In next month’s column, we will complete this process and also (hopefully) add the eight push-button switches at the bottom of the console’s front panel (at lower right in Photo 1). Initially, we will connect all these components directly to our Arduino Uno to make sure everything functions as planned. Holding it together I’m hoping that, like me, you’re working with a laser-cut front panel. I’m also hoping that, in the fullness of time, you plan on mounting this panel in a custom 3D-printed enclosure. However, if you don’t have access to a laser cutter and a 3D printer (or a well-equipped friend), you can always attach everything to something like a piece of hardboard (aka pressboard), or ¼-inch (6.35mm) thick plywood and call it a prototype. Always remember that a hot glue gun is a tool of champions. That said, never apply hot glue to electronic components while the circuit is powered, as the heat can cause damage or even trigger thermal runaway in sensitive devices. I learned this the hard way several years ago with some tricolour LEDs in Adafruit NeoPixel rings. I won’t do that again. One step at a time One of the lessons we’ve (hopefully) learned during our time together is that when we’re working on a new circuit, or moving from one version to the next, it’s almost always better to change just one thing at a time. This is one of those engineering wisdom things that applies to the prototyping and debugging of both hardware and software. Why? Well, if we tweak a bunch of things at the same time, and whatever we’re working on starts to misbehave, we won’t know exactly what caused the problem. That can send us down a rabbit hole of time-­consuming guesswork. Alternatively, if we take things step by step, every change teaches us something. We’ll spot problems immediately, avoid breaking something that was already working, and develop a better understanding of how our hardware or software behaves. Working this way might feel slower in the moment, but it saves a ton of time (and frustration) in the long run. Taking our first step Photo 1: an early version of our retro games console (Photograph by Joe Farr). Thus far, we’ve been using a 74HC595 8-bit serial-in, parallelout (SIPO) shift register as part of our breadboard-based experiments (Fig.1). Different data sheets may refer to the outputs from this device as a to h, A to H, QA to QH, or Q0 to Q7. In our case, we may switch between referring to things like “bit 0”, “output 0” or “output a”, depending on what works best in the given context. As a brief reminder, the shift register device contains two 8-bit registers. We refer to one of these as the ‘shift register’ (which can be a 26 Practical Electronics | November | 2025 SRCLR b 1 16 VCC c 2 15 a d 3 14 SER SER e 4 13 OE RCLK f 5 12 RCLK g 6 11 SRCLK h 7 10 SRCLR GND 8 9 h’ (a) Package SRCLK 0 1 2 3 4 5 6 7 Shift register h’ 0 1 2 3 4 5 6 7 Output register OE Connection No connection a b c d e f g h (b) Block diagram Fig.1: the pinout and internal structure of a 74HC595 serial-to-parallel shift register. Also observe that since we are no longer controlling the shift register’s active-low SRCLR input with the Arduino, we’ve added a 10kΩ pull-up resistor connecting this pin to the +5V rail, placing it in its inactive state. You can see a full breadboard layout in the file named CB-nov25-brd-01. pdf. As usual, all files mentioned in this column are available as part of the November 2025 download package from the PE website at https:// pemag.au/link/ac8h available to us. This means that when Make it count! How about we create a quick prowe wish to clear the device, we will gram that loops around counting from need to shift in a series of 0 values. Additionally, we began our experi- 0 to 255 (that’s 00000000 to 11111111 ments by connecting the SER input to in binary or 00 to FF in hexadecilogic 1. Later, when we implement- mal), displaying each value on our ed a Johnson counter, followed by a 8-segment display and pausing for linear feedback shift register (LFSR), one second between values? “That’s we used the output from another a great idea”, I hear you say. You’re logic device to feed the SER input. right, it is, so you go first, and we’ll Now, we’re going to drive this input compare our code at the end. You’re ready? Wow, that was fast! directly from our Arduino. In our most recent experiments, we You’re getting good at this. This program is short enough that used Arduino digital input/output (I/O) pins 5, 6, and 7 to drive the it’s probably worth going through in shift register control inputs SRCLR, its entirety, not least because it will SRCLK and RCLK, respectively. This soon form the basis for driving the made sense at the time, but this pin displays on our games console. You group is slap-bang in the middle of can download my version in the file the digital pins. Since we are soon named CB-nov25-code-01.txt. going to be using all of these pins, As usual, we start with definithat’s a tad inconvenient. tions, then declare and assign pins First, we'll remove all the extraneous OE SRCLR parts from our breadboard, leaving only the 74HC595 shift register, 8-segment LED bar graph display, and any associated resistors and wires. We will use 74HC595 Arduino pins 11, 12, and 13 to drive the shift register’s SER, RCLK and SRCLK inputs, respectively. You should aim to achieve a layout that resembles the one illustrated in Fig.2. Observe that the Pin 13 = SRCLK 10kΩ pull-down rePin 12 = RCLK sistor connecting the Pin 11 = SER shift register’s activelow OE to the 0V rail stays as-is. This is because, for our purposes, we always want Arduino DIGITAL IN/OUT (PWM ~) this pin to be in its active state, thereby Orange LED Green LED ensuring that the shift register’s outputs are always enabled. Fig.2: our new cut-down breadboard setup. Practical Electronics | November | 2025 27 AREF GND 13 12 ~11 ~10 ~9 8 7 ~6 ~5 4 ~3 2 TX-1 RX-0 tad confusing), and the other as the ‘output register’. The operation of this component can be summarised as follows. A logic 0 on the active-low SRCLR (shift register clear) input loads all the bits in the shift register with 0. A rising 0 → 1 transition on the positive­-edge-triggered SRCLK (shift register clock) input causes the contents of the shift register to be shifted one bit (to the right in this illustration). Whatever 0 or 1 value that’s currently on the SER (serial data in) input is copied into bit 0. At the same time, the original contents of bit 0 are copied into bit 1, the contents of bit 1 are copied into bit 2, and so on down the line. The original contents of bit 7 conceptually ‘fall off the end’ (or form the input to the next shift register device in the chain). A rising 0 → 1 transition on the positive-edge-triggered RCLK (output register clock) copies all the bit values in the shift register into the corresponding bits in the output register. Finally, a logic 0 on the shift register’s active-low OE (output enable) input allows whatever values are stored in the output register to be made available on the 74HC595’s output pins. By comparison, applying a logic 1 to this input causes the output pins to be placed in a tri-state (high-impedance) condition, effectively disconnecting the output buffers from the outside world. It’s important to note that the OE signal does not affect the values stored in the output register, only the values visible to the outside world. By some strange quirk of fate, the 74HC595 is the same device (albeit in a smaller, surface-mount package) that’s featured in the display modules we’re going to add to our games console, but we’re going to do a few things differently from them. For example, thus far, we’ve been taking advantage of the active-low SRCLR input to clear our shift register. However, the creators of the display modules have not made this signal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // Display binary count on SR // Display Stuff #define NUM_SEGS 8 // Shift Register (SR) Stuff #define SR_INACTIVE LOW #define SR_ACTIVE HIGH // Extras #define UPDATE_RATE 1000 // Declare pins driving the SR const int PinSER = 11; const int PinRCLK = 12; const int PinSRCLK = 13; // Declare global variables uint8_t CountVal = 1; Listing 1(a): definitions and declarations. and global variables, as shown in Listing 1(a). Remember that we are using the convention that the listing number (1 in this case) corresponds to the numerical part of the matching code file name (“01” here). On line 4, we define NUM_SEGS (the number of segments in our bar graph display) to be 8. We’ve seen this before. The only reason I mention it here is that this will also work with (apply to) our 7-segment displays in the future. Recall that these have seven main segments along with an extra decimal point segment. Since all the shift register’s activelow inputs are now hard-wired to their appropriate logic values, I’ve decided to simplify things by defining common active and inactive states on lines 7 and 8. I’ve also defined the update (refresh) rate as being 1000ms (one second) on line 11. On lines 14, 15, and 16, we declare the names of the variables we’re going to use to drive the shift register’s SER, RCLK and SRCLK inputs, and assign them pin numbers 11, 12 and 13, respectively. On line 19, we declare an 8-bit unsigned integer called CountVal and assign it an initial value of 1 (we introduced fixed-width data types like uint8_t in the May 2025 column). Earlier, we stated that our counter will loop around, counting from 0 to 255, and it will certainly do so on subsequent iterations. We’ve initialised CountVal with a value of 1, meaning we will count from 1 to 255 on the first iteration, because this will facilitate comparisons between the actions of this program and those of our followup experiment. Now let’s consider the three utility functions shown in Listing 1(b). We’ll commence with the ClearSR (clear shift register) function, which starts on line 48. This accepts an 8-bit unsigned integer parameter called numBits, which contains the number of bits in the shift register. 28 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 // Clear the shift register void ClearSR (uint8_t numBits) { // Set serial data to 0 digitalWrite(PinSER, SR_INACTIVE); for (int iBit = 0; iBit < numBits; iBit++) { digitalWrite(PinSRCLK, SR_ACTIVE); digitalWrite(PinSRCLK, SR_INACTIVE); } } // Load 8-bit value into the shift register void LoadSR (uint8_t thisByte) { for (int iSeg = 0; iSeg < NUM_SEGS; iSeg++) { digitalWrite(PinSER, (thisByte & 1)); digitalWrite(PinSRCLK, SR_ACTIVE); digitalWrite(PinSRCLK, SR_INACTIVE); thisByte >>= 1; } } // Copy shift register into output register void LoadOR () { // Load the output register digitalWrite(PinRCLK, SR_ACTIVE); digitalWrite(PinRCLK, SR_INACTIVE); } Listing 1(b): some utility functions for manipulating the shift register IC. Since we are currently using a single ‘595 integrated circuit (IC or ‘chip’) to implement our shift register, we know that it contains only eight bits. The reason for specifying the number of bits as a parameter is to future-proof this portion of our code, allowing it to support a chain of multiple cascaded ‘595 chips without modification. On line 51, we set the shift register’s SER input to 0. On lines 53 to 57, we apply the required number of positive-­going pulses to the shift register’s SRCLK input to load all its bits with 0s. Now let’s turn our attention to the LoadSR() (load shift register) function, which starts on line 61. This accepts an 8-bit unsigned integer called thisByte, containing the value we wish to display. On line 63, we declare a for() loop that will execute eight times (because 8 is the value we’ve assigned to NUM_SEGS). Each time around this loop, we perform four tasks. On line 65, we use the & (AND) operator to mask out the least-significant bit (LSB) of thisByte. You can think of this as (thisByte & 0b00000001). The result, 0 or 1, corresponds to the value in the LSB. This is written to the shift register’s SER input. On lines 66 and 67, we apply a positive-going pulse to the shift register’s SRCLK input, shifting the value on the SER input into the register. Then, on line 68, we shift the value stored in thisByte one bit to the right, discarding the LSB we just used and bringing the next bit into the LSB position, ready for the next pass through the loop. Consider the LoadOR( ) (load the output register) function that commences on line 73. This simply applies a positive-going pulse to the shift register’s RCLK input, copying the contents of the shift register into the output register, at which point they become visible to the outside world. Now that we have all this under our belt, we can turn our attention to the setup() and loop() functions, illustrated in Listing 1(c). The workings of both of these functions are reasonably self-explanatory. In the case of the setup( ) function, we begin by initialising the pins driving the shift register. Then we call our ClearSR( ) function on line 34 to clear all the shift register bits to 0, followed by a call to our LoadOR( ) function on line 35 to copy the bits from the shift register into the output register. At this point, they will appear on our display. When it comes to the loop( ) function, on line 41, we call our LoadSR( ) function to load the shift register with the current value in CountVal. On line 42, we call the LoadOR( ) function to copy this value into the output register, at which point it will appear on our display. On line 43, we increment (add one to) the value in CountVal. Since this is an 8-bit unsigned number, when it reaches 255 (11111111 in binary), the next increment will cause it to ‘roll over’ and contain 0 (00000000 in binary). Finally, on line 44, we pause for a moment to allow us to peruse Practical Electronics | November | 2025 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 27 = 128 // Do this one time void setup () { // Initialize the SR pins pinMode(PinSER, OUTPUT); pinMode(PinRCLK, OUTPUT); pinMode(PinSRCLK, OUTPUT); (Bit 7) MSB digitalWrite(PinSER, SR_INACTIVE); digitalWrite(PinRCLK, SR_INACTIVE); digitalWrite(PinSRCLK, SR_INACTIVE); // Clear the shift register ClearSR(NUM_SEGS); LoadOR(); LSB (Bit 0) Cleared =0 = 0 Cycle 1 =1 = 1 Cycle 2 =2 = 2 Cycle 3 =3 = 4 Cycle 4 =4 = 8 Cycle 5 =5 = 16 Cycle 6 =6 = 32 Cycle 7 =7 = 64 Cycle 8 =8 = 128 } // Do this over and over again void loop () { LoadSR(CountVal); LoadOR(); CountVal += 1; delay(UPDATE_RATE); } Listing 1(c): the setup() and loop() functions of our test sketch. 35 36 37 38 39 40 41 42 43 44 45 46 47 20 = 1 // Do this over and over again void loop () { uint8_t tmpVal = 0b00000001; for (int iSeg = 0; iSeg < NUM_SEGS; iSeg++) { LoadSR(tmpVal); LoadOR(); delay(UPDATE_RATE); tmpVal <<= 1; } : Listing 2(a): implementing a sliding 1 display scheme. Mental gymnastics To be honest, wrapping one’s brain around the way in which all this works can require some mental gymnastics, so you might want to perform some limbering-up exercises before we proceed into the full intellectual floor routine. In the case of a general-purpose register or a binary data value, the domiPractical Electronics | November | 2025 : (b) Walking 1 Fig.3: the bargraph display with two test sketches. } and ponder the segments on the display before proceeding to process the next count value. It’s important to wrap our brains around what we are doing here, which is to first load all eight bits into the shift register, and only then copy the contents of the shift register into the output register. If we loaded the output register every time a new bit was loaded into the shift register, this would result in ‘ghosting’ on the 8-segment display. That will be due to brief, unintended flashes where incorrect segments appear momentarily before the full, correct pattern is established. Run this program and observe the binary count being presented on the 8-segment display as illustrated in Fig.3(a). I don’t know about you, but I could happily watch this for hours. : (a) Binary count nant convention in illustrations and our mental models is to consider the least-significant bit (LSB) to be on the right and the most-significant bit (MSB) to be on the left. This is how we think about our CountVal variable, as illustrated in Fig.4(a), and our 8-segment display, as shown in Fig.4(c). Unfortunately, things become a little fuzzier in the case of shift registers like the 74HC595. This is one of those areas where context and convention can collide. In shift register datasheets, manufacturers typically avoid explicitly using the terms LSB or MSB, as these depend on the user’s shifting convention and wiring arrangement. The data sheets just show QA to QH and the movement of data. This means that whether QA is treated as the LSB and QH is treated as the MSB, or vice versa, depends on how we choose to shift the data in (LSB-first or MSB-first), and how we decide to employ the outputs. In our case, the LoadSR( ) function shifts the CountVal value into the shift register starting with the LSB first. This means the LSB of CountVal ends up on output QH, and the MSB of CountVal ends up on output QA. Since we’ve connected QH and QA to our display’s LSB and MSB, respectively, everything works as we want it to (hurrah!). Slip-sliding along For reasons that will become apparent shortly, we are going to modify our program to display what we might think of as a “Walking 1” (or a “Sliding 1”) pattern, as illustrated in Fig.3(b). This is easy-peasy lemon squeezy; you can access the new version of this program in the file named CB-nov25code-02.txt. We no longer need our global CountVal variable, so we can remove it. Everything else remains the same, except for our loop( ) function, which has been updated to the new version shown in Listing 2(a). MSB LSB 7 6 5 4 3 2 1 0 (a) CountVal 0 1 2 3 4 5 6 7 (b) Shift Register a b c d e f g h (c) Display MSB LSB Fig.4. this can make your head hurt. 29 We start on line 38 by declaring an 8-bit unsigned integer called tmpVal and loading it with binary 00000001. We could have simply said tmpVal = 1, but the binary representation helps us better visualise what’s going on. On line 44, we declare a for( ) loop that will execute eight times (because 8 is the value we’ve assigned to NUM_SEGS). Each time around the loop, we perform four tasks. On line 42, we call our LoadSR( ) function to load the current value in tmpVal into our shift register. On line 43, we call the LoadOR( ) to copy the bits from the shift register into the output register, at which point they will appear on our display. On line 44, we pause to ponder the segments on the display. Finally, on line 45, we shift the contents of tmpVal one bit to the left. This will automatically shift a 0 into the LSB, while the MSB will conceptually ‘fall off the end’ and be discarded. The result is a classic “Walking 1” pattern, in which our 1 travels from right to left on our display. If we ever wanted to reverse direction and have the 1 move from left to right, we could do so by changing the assignment value on line 38 to 0b10000000, and modifying line 45 to implement a shift right (tmpVal >>= 1;). We c o u l d h a v e s i m p l y clocked a logic 1 across the display ‘by hand’, as it were. To do this, we would first place a 1 on the SER input, clock it in with SRCLK, and load the output register with RCLK. Then we would place a 0 on the SER input and repeatedly clock SRCLK and RCLK to walk the 1 across the display. Yo u c o u l d t r y doing this yourVCC (+5V) VCC self. For our purposGND (0V) GND es here, however, I SDI SDO felt it was better to SCLK SCLK stick with our utiliLOAD LOAD ty functions because these are what we will be using in the future. After all, the Fig 5: the pinout of a two-digit 7-segment display module. more we use them, the more familiar we become with the way in which they perform their magic. Run this program and verify that you see the sequence illustrated in Fig.3(b) on your display before proceeding to the exciting part. Soldered pins Have you got the modules? Header I can’t help myself. I’m thinking pin of the old comedy duo Morecambe and Wise (Eric Morecambe looked just like my dear old dad). In parFig 6: adding header pins to our module. ticular, the sketches where Ernie associated current-limiting resistors Wise would ask a question along the and the 74HC595 shift register chips to lines of, “Have you got the scrolls?” drive them. However, it’s much easier (or “modules,” in our case), and Eric to work with pre-built modules that would respond, “No, I always walk already have the components mountthis way!” (I’m easily amused). ed on a small circuit board. As we discussed in our January Modules of this type are available 2025 column, when it comes to the 7-­segment displays used on our games from multiple suppliers, but we would console, we could opt to use individbe hard-pressed to beat the prices on ual devices, along with AliExpress (https://pemag.au/link/ ac8i). These modules are available with two, three or four digits (see Photo 2). I opted to use three two-digit modules to match the prototype console that was constructed by my friend Joe Farr (shown in Photo 1). However, you can choose to use as few or as many of these two-, three- or fourdigit modules as you wish. A close-up view of one of my twodigit modules is shown in Fig.5. The SDI (serial data in), SCLK (shift register clock) and LOAD signals are equivalent to our existing SER, SRCLK, and RCLK signals, respectively. The SDO (serial data out) pin can be used to feed the next module in the chain. Mounting the components Photo 2. multi-digit 7-segment LED display modules (source: AliExpress). Just to make sure we know what we’re doing (and I use this phrase in the loosest possible sense), we will add five 0.1-inch (2.54mm) pitch header pins (https://pemag.au/link/ ac8j) to the input side of one of our modules (the side with the SDI input), as illustrated in Fig.6. Next, we will ensure that everything is powered down and then attach this module to our breadboard, as illustrated in Fig.7. Observe that we’ve added red and black wires to connect the +5V and 0V pins on the module to the +5V and 0V rails on the breadboard, respectively. 30 Practical Electronics | November | 2025 OE SRCLR 74HC595 Pin 13 = SRCLK From Arduino Pin 12 = RCLK Pin 11 = SER Fig 7: adding the module to our breadboard. 3 4 5 6 // Display Stuff #define NUM_SEGS 8 // Segments per display #define NUM_DIGS 2 // Digits (displays) #define ALL_SEGS (NUM_SEGS * NUM_DIGS) Listing 3(a): some additional definitions near the top of our final test sketch. Additionally, we’ve added pink wires to extend the SER, SRCLK, and RCLK signals from the ‘595 chip on our breadboard to also feed the SDI, SCLK and LOAD pins on the module. A full breadboard layout can be found in the file named CBnov25-brd-02.pdf. The time has come! We’re poised to start driving our 7-segment display module. First, we need to make a minor tweak to our program, shown in Listing 3(a). A full listing is provided in the file named CB-nov25-code-03.txt. As we noted earlier, our original NUM_SEGS definition on line 4 remains relevant because it applies both to our old bar graph display and to each of our new 7-segment displays, which include an eighth decimal point (DP) segment. Shift direction A F Our new NUM_DIGS definition specifies the number of 7-segment digits we’re working with (currently two). This leads us to the ALL_SEGS definition, which specifies the total number of segments in our 7-segment display chain. The C/C++ preprocessor will automatically evaluate this to be 8 × 2 = 16. While the compiler doesn’t enforce it, it’s imperative to include the ( ) round brackets (parentheses) in this type of macro definition. Otherwise, strange things can happen when we least Cleared expect them. If you look at the program listing, you’ll see Cycle 1 that we pass ALL_SEGS as an argument into the Cycle 2 call to the ClearSR( ) function from the setup() function. Cycle 3 B Cycle 5 A B C D E F G * Display Cycle 6 0 1 2 3 4 5 6 7 Shift a b c d e f g h Register Cycle 7 G E C (c) D (a) DP I didn’t see that coming! I wasn’t sure whether to expect the sequence shown in Fig.9(b) or Fig.9(c). You can only imagine my surprise at seeing the sequence depicted in Fig.9(d)! Cycle 4 0 1 2 3 4 5 6 7 Shift a b c d e f g h Register (b) Let’s pause for a moment to consider what we expect to see before running this new version of our program. I’ll give you a clue. We don’t know how the makers of our 7-segment display modules decided to connect the display chips to the shift register chips. From our Arduino Bootcamp columns, we know that the segments on the displays are referred to as A, B, C, D, E, F, G and DP, as illustrated in Fig.8(a). What we don’t know in the case of our modules is whether output a from the shift register drives segment A on the display, as shown in Fig.8(b). In this case, we would expect to see results as per Fig.9(b). Alternatively, if output h from the shift register drives segment A on the display, like in Fig.8(c), we would expect to see results like in Fig.9(c). This is another example of a situation that requires some mental agility on our part. Remember that we aren’t shifting bits individually. The way our program is written, we shift an entire byte into the shift register before loading (copying) it into the output register. Apart from anything else, this explains why the right-hand digit in the display pair lags the lefthand digit by one cycle. When you’re ready, run the program and observe the results. Cycle 8 * G F E D C B A Display * = Decimal Point (DP) Fig.8: how are our shift registers connected? Practical Electronics | November | 2025 : : (a) : (b) : (c) : (d) Fig.9: what’s connected to what? 31 Multiple anodes A A B C D E F G DP F G DP (b) F B G E Common cathode CC) 0V C D (a) Common anode (CA) 5V DP (c) A B C D E Multiple cathodes This means that the shift registers in my modules are connected to their 7-segment displays as shown in Fig.8(b). However, the displays themselves aren’t the common cathode (CC) types we grew to know and love in our Arduino Bootcamp columns, like in Fig.10(b). No siree Bob! These displays are of the common anode (CA) persuasion, per Fig.10(c). In the case of common cathode displays, which I initially thought I was using, a logic 1 (HIGH) switches the corresponding segment on, while a logic 0 (LOW) turns it off. Things work the other way around with the common anode displays, which I find I’m actually using. In this case, a logic 1 switches the segment off while a logic 0 lights it up. Spoiled for choice Fig.10: my displays are of the common anode type. Fig.11: three different ways to resolve the CA vs CC LED array problem. There are multiple ways that we can resolve the CA vs CC issue. One approach would be to change our “Walking 1”, shown in Fig.11(a), into a “Walking 0”, as depicted in Fig.11(b). This would involve two tweaks to our code. The first occurs on line 41, where we change our initial assignment from 0b00000001 to 0b11111110. The second occurs on line 48, which is where we perform our shift. The problem here is that the shift operator will automatically cause a 0 to be inserted into the least-significant (right-hand) bit. In this case, however, we wish to insert a 1. To achieve this, we perform the shift, which inserts a 0, and then use the | operator to bitwiseOR the shifted value with 0b00000001, which will force the least significant bit to be 1. Different people approach these things in various ways. Personally, I dislike the “Walking 0” solution because my own mental model equates 1 with on and 0 with off. For this reason, I prefer to stick with using a “Walking 1” but invert the 8-bit tmpVal value just before passing it into our LoadSR( ) function, as illustrated on line 45 in Fig.11(c). Note that, while this causes an inverted copy of tmpVal to be passed into the function, it has no effect on the contents of tmpVal itself. In this case, I’m using the idiomatic ~ bitwise-NOT operator to invert the bit pattern stored in tmpVal. It swaps each 0 for 1 and vice versa. We could achieve the same effect in other ways. For example, we could subtract the value in tmpVal from 255 in decimal (0b11111111 in binary or 0xFF in hexadecimal). For example, 42 = 00101010 in binary, and 255 – 42 = 213, which is 11010101 in binary (I feel a little “tra-la” is called for). 32 Practical Electronics | November | 2025 35 36 37 38 39 40 41 42 43 44 45 46 47 // Do this over and over again void loop () { uint8_t tmpVal = 0b00000001; for (int iSeg = 0; iSeg < NUM_SEGS; iSeg++) { LoadSR(tmpVal); LoadOR(); delay(UPDATE_RATE); tmpVal <<= 1; } } Iterations 0: 00000001 1: 00000010 2: 00000100 3: 00001000 4: 00010000 5: 00100000 6: 01000000 7: 10000000 (a) Option 1: original "walking 1" code. 35 36 37 38 39 40 41 42 43 44 45 46 47 // Do this over and over again void loop () { uint8_t tmpVal = 0b11111110; for (int iSeg = 0; iSeg < NUM_SEGS; iSeg++) { LoadSR(tmpVal); LoadOR(); delay(UPDATE_RATE); tmpVal = (tmpVal << 1) | 0b00000001; } } Iterations 0: 11111110 1: 11111101 2: 11111011 3: 11110111 4: 11101111 5: 11011111 6: 10111111 7: 01111111 (b): Option 2: employ a "walking 0" strategy. 35 36 37 38 39 40 41 42 43 44 45 46 47 // Do this over and over again void loop () { uint8_t tmpVal = 0b00000001; for (int iSeg = 0; iSeg < NUM_SEGS; iSeg++) { LoadSR(~tmpVal); LoadOR(); delay(UPDATE_RATE); tmpVal <<= 1; } (~tmpval); or (255 - tmpVal) or (tmpVal^0xFF) } (c) Option 3: invert the value passed to the LoadSR() function. Devices and components used in this issue Cleared https://pemag.au/link/ac2g https://amzn.to/3O2L3e8 Arduino Uno R3 microcontroller module Solderless breadboard 4-inch (10cm) jumper wires (optional) 8-inch (20cm) jumper wires (male-to-male) LEDs (assorted colours) Resistors (assorted values) Ceramic capacitors (assorted values) 16V 100µF electrolytic capacitors 8-Segment DIP red LED bar displays 74HC595 8-bit shift registers Dual 7-segment display modules 0.1” pitch header pins Cycle 1 Cycle 2 Cycle 3 Cycle 4 https://pemag.au/link/ac2l https://amzn.to/3O4hnxk https://amzn.to/3E7VAQE https://amzn.to/3O4RvBt https://pemag.au/link/ac2i https://pemag.au/link/ac2j https://pemag.au/link/ac2c https://pemag.au/link/ac1n https://pemag.au/link/ac8i https://pemag.au/link/ac8j Cycle 5 I've no idea why they call me "Max Max"! Cycle 6 Cycle 7 Cycle 8 : : : Fig.12: finally, what I was expecting! Yet another alternative is to use the ^ bitwise-XOR operator, ie, (tmpVal ^ 255) or (tmpVal ^ 0xFF) or (tmpVal ^ 0b11111111). This will cause each bit in tmpVal to be XORed with 1, thereby inverting it. You’ll see why this works if you look at the XOR truth tables we showed in earlier columns. I’ve created an updated version of our program that continues to use a “Walking 1” and inverts the value as it’s passed into the LoadSR( ) function. A full listing is provided in the file named CB-nov25-code-04.txt. When I ran this latest incarnation of my program, I saw the sequence shown in Fig.12. Hooray! Now we’re really cooking with a low-density flammable hydrocarbon! It’s necessary to note that, in the case of the cheap-and-cheerful display modules we’re using here, any datasheet documentation may be sadly lacking. This means that the modules you purchase could be wired as shown in Fig.8(b) or Fig.8(c). Also, they may turn out to be either CC or CA types, as depicted in Fig.10(b) or Fig.10(c), respectively. You may not discover the nitty-gritty details until you have these modules in your hot little hands, so you will need to perform your own tests and modify your programs accordingly. We’ll talk more about this in our next column, speaking of which… FIND ALL YOUR ELECTRONIC COMPONENTS IN ONE PLACE Next time In our next column, we will begin displaying real alphanumeric values on our 7-segment displays. Additionally, we will incorporate a full complement of these modules into our games console. There’s also a batting chance we’ll be adding the eight push-button switches to the console’s front panel. As always, if you have any thoughts you’d care to share on anything you’ve read here, feel free to drop me an email PE at max<at>clivemaxfield.com BASIC MICRO E L E CT R O N I C S C O M P O N E N T S U P P L I E R w w w . basicmicro . co . u k High-quality, genuine parts Practical Electronics | November | 2025 33