Silicon ChipMax’s Cool Beans - May 2021 SILICON CHIP
  1. Outer Front Cover
  2. Contents
  3. Subscriptions: PE Subscription
  4. Subscriptions: PicoLog Cloud
  5. Back Issues: PICOLOG
  6. Publisher's Letter
  7. Feature: The Fox Report by Barry Fox
  8. Feature: Techno Talk by Mark Nelson
  9. Feature: Net Work by Alan Winstanley
  10. Project: 7-Band Mono or Stereo Equaliser by John Clarke
  11. Project: Touchscreen car altimeter by Peter Bennett
  12. Project: DIY Solder ReFLow Oven with PID Control by Phil Prosser
  13. Feature: Max’s Cool Beans by Max the Magnificent
  14. Feature: Make it with Micromite by Phil Boyce
  15. Feature: PICn’Mix by Mike Hibbett
  16. Feature: AUDIO OUT by Jake Rothman
  17. Feature: Circuit Surgery by Ian Bell
  18. Feature: Practically Speaking by Jake Rothman
  19. PCB Order Form
  20. Advertising Index: Max’s Cool Beans cunning coding tips and tricks

This is only a preview of the May 2021 issue of Practical Electronics.

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

Articles in this series:
  • (November 2020)
  • (November 2020)
  • Techno Talk (December 2020)
  • Techno Talk (December 2020)
  • Techno Talk (January 2021)
  • Techno Talk (January 2021)
  • Techno Talk (February 2021)
  • Techno Talk (February 2021)
  • Techno Talk (March 2021)
  • Techno Talk (March 2021)
  • Techno Talk (April 2021)
  • Techno Talk (April 2021)
  • Techno Talk (May 2021)
  • Techno Talk (May 2021)
  • Techno Talk (June 2021)
  • Techno Talk (June 2021)
  • Techno Talk (July 2021)
  • Techno Talk (July 2021)
  • Techno Talk (August 2021)
  • Techno Talk (August 2021)
  • Techno Talk (September 2021)
  • Techno Talk (September 2021)
  • Techno Talk (October 2021)
  • Techno Talk (October 2021)
  • Techno Talk (November 2021)
  • Techno Talk (November 2021)
  • Techno Talk (December 2021)
  • Techno Talk (December 2021)
  • Communing with nature (January 2022)
  • Communing with nature (January 2022)
  • Should we be worried? (February 2022)
  • Should we be worried? (February 2022)
  • How resilient is your lifeline? (March 2022)
  • How resilient is your lifeline? (March 2022)
  • Go eco, get ethical! (April 2022)
  • Go eco, get ethical! (April 2022)
  • From nano to bio (May 2022)
  • From nano to bio (May 2022)
  • Positivity follows the gloom (June 2022)
  • Positivity follows the gloom (June 2022)
  • Mixed menu (July 2022)
  • Mixed menu (July 2022)
  • Time for a total rethink? (August 2022)
  • Time for a total rethink? (August 2022)
  • What’s in a name? (September 2022)
  • What’s in a name? (September 2022)
  • Forget leaves on the line! (October 2022)
  • Forget leaves on the line! (October 2022)
  • Giant Boost for Batteries (December 2022)
  • Giant Boost for Batteries (December 2022)
  • Raudive Voices Revisited (January 2023)
  • Raudive Voices Revisited (January 2023)
  • A thousand words (February 2023)
  • A thousand words (February 2023)
  • It’s handover time (March 2023)
  • It’s handover time (March 2023)
  • AI, Robots, Horticulture and Agriculture (April 2023)
  • AI, Robots, Horticulture and Agriculture (April 2023)
  • Prophecy can be perplexing (May 2023)
  • Prophecy can be perplexing (May 2023)
  • Technology comes in different shapes and sizes (June 2023)
  • Technology comes in different shapes and sizes (June 2023)
  • AI and robots – what could possibly go wrong? (July 2023)
  • AI and robots – what could possibly go wrong? (July 2023)
  • How long until we’re all out of work? (August 2023)
  • How long until we’re all out of work? (August 2023)
  • We both have truths, are mine the same as yours? (September 2023)
  • We both have truths, are mine the same as yours? (September 2023)
  • Holy Spheres, Batman! (October 2023)
  • Holy Spheres, Batman! (October 2023)
  • Where’s my pneumatic car? (November 2023)
  • Where’s my pneumatic car? (November 2023)
  • Good grief! (December 2023)
  • Good grief! (December 2023)
  • Cheeky chiplets (January 2024)
  • Cheeky chiplets (January 2024)
  • Cheeky chiplets (February 2024)
  • Cheeky chiplets (February 2024)
  • The Wibbly-Wobbly World of Quantum (March 2024)
  • The Wibbly-Wobbly World of Quantum (March 2024)
  • Techno Talk - Wait! What? Really? (April 2024)
  • Techno Talk - Wait! What? Really? (April 2024)
  • Techno Talk - One step closer to a dystopian abyss? (May 2024)
  • Techno Talk - One step closer to a dystopian abyss? (May 2024)
  • Techno Talk - Program that! (June 2024)
  • Techno Talk - Program that! (June 2024)
  • Techno Talk (July 2024)
  • Techno Talk (July 2024)
  • Techno Talk - That makes so much sense! (August 2024)
  • Techno Talk - That makes so much sense! (August 2024)
  • Techno Talk - I don’t want to be a Norbert... (September 2024)
  • Techno Talk - I don’t want to be a Norbert... (September 2024)
  • Techno Talk - Sticking the landing (October 2024)
  • Techno Talk - Sticking the landing (October 2024)
  • Techno Talk (November 2024)
  • Techno Talk (November 2024)
  • Techno Talk (December 2024)
  • Techno Talk (December 2024)
  • Techno Talk (January 2025)
  • Techno Talk (January 2025)
  • Techno Talk (February 2025)
  • Techno Talk (February 2025)
  • Techno Talk (March 2025)
  • Techno Talk (March 2025)
  • Techno Talk (April 2025)
  • Techno Talk (April 2025)
  • Techno Talk (May 2025)
  • Techno Talk (May 2025)
  • Techno Talk (June 2025)
  • Techno Talk (June 2025)
Max’s Cool Beans By Max the Magnificent Flashing LEDs and drooling engineers – Part 15 E very now and then,* a once- in-a-generation mind appears on the scene (*by some strange quirk of fate, this occurs approximately once in a generation). Such a mind sees things beyond the ken of the average Joe or Josephina, shining the bright light of understanding on previously unsurmountable problems, rending the veils asunder, and revealing the mysteries of the universe. What? Who? Me? Well, you are very kind, and I can see how you might have come to that conclusion (especially if you’ve been talking to my dear old mother), but I was actually talking about the English mathematician John Horton Conway (1937–2020), who made his appearance in the generation before mine, thereby ensuring that future historians would not be obliged to pick and choose between us. Speaking of generations, I’m reminded of the question I posed in, With Which Generation Do You Identify Yourself? (https://bit.ly/3brBsfv), where I asked: ‘Are you from the Silent Generation, a Baby Boomer, a member of the Generation Jones crew, a Millennial, or do you have ‘Generation X, Y, or Z’ tattooed on your forehead?’ As for myself, I’m a proud member of Generation Jones (seriously; it really is a thing – http:// bit.ly/318NfcD). It’s alive! Conway was a larger-than-life character who has been described as ‘Archimedes, Mick Jagger, Salvador Dali, and Richard Feynman all rolled into one.’ Among his many contributions to diverse branches of mathematics, Conway invented (maybe we should say ‘discovered’) the ‘surreal numbers’. As the Wikipedia observes, ‘If formulated in Von Neumann-BernaysGödel set theory, the surreal numbers are a universal ordered field in the sense that all other ordered fields, such as the rationals, the reals, the rational functions, the Levi-Civita field, the superreal numbers, and the hyperreal numbers, can be realised as subfields of the surreals.’ Well, duh! Of particular interest to us here is that Conway was the originator of a famous 40 The great John Conway observing his Game of Life cellular automoton program on an early computer screen. two-dimensional cellular automaton The first generation is created by ap(plural cellular automata, abbreviation plying the above rules simultaneously to CA) now known as Conway’s Game of every cell in the seed generation. Births Life (GOL), which he unleashed on the and deaths occur simultaneously, and the world in 1970. The ‘universe’ in which discrete moment at which this happens is the GOL takes place is an infinite, twosometimes called a ‘tick.’ Each generation dimensional orthogonal grid of square ‘cells,’ each of which is in one of two possible states, ‘live’ or ‘dead.’ Every cell interacts with D ead c ell its eight ‘neighbours,’ which are L iv e c ell those cells that are horizontally, vertically or diagonally adjacent. At the beginning of the game, ( a) S eed ( G eneration 0 ) the universe is populated with an initial pattern, which constitutes L iv e c ell with 0 or 1 liv e neighbors the ‘seed’ of the system. The game L iv e c ell with 2 liv e neighbors then evolves from generation to L iv e c ell with 3 liv e neighbors generation according to the folL iv e c ell with > 3 liv e neighbors lowing rules: D ead c ell with 3 liv e neighbors 1. Any live cell with fewer than ( b) E v aluating G eneration 1 two live neighbours dies, as if by underpopulation. 2. Any live cell with two or three live neighbours lives on to the D ead c ell next generation. L iv e c ell 3. Any live cell with more than three live neighbours dies, as ( c ) G eneration 1 if by overpopulation. 4. Any dead cell with exactly three live neighbours becomes a live Fig.1. A simple example reflecting a small portion cell, as if by reproduction. of the universe. Practical Electronics | May | 2021 ( a) S eed ( G eneration 0 ) ( b) G eneration 1 ( b) G eneration 2 ( b) G eneration 3 ( b) G eneration 4 Fig.2. This Glider translates across the grid in a south-easterly direction. is a pure function of the preceding one. The rules continue to be applied repeatedly to create further generations. Let’s consider a simple example to get our mental juices flowing (don’t worry, we’ll mop everything up later). We’ll start by looking at a small part of the universe that’s been seeded as illustrated in Fig.1a. We will consider this to be Generation 0. Next, we start to evaluate the new generation, which will be Generation 1 in this case (Fig.1b). As we see, there’s one live cell that doesn’t have any neighbours, so that cell won’t make it to the next generation (as if by underpopulation). We have one live cell that has two live neighbours and two live cells that have three live neighbours, all of which will survive to the next generation. We have two live cells with four live neighbours, which means they won’t survive (as if by overpopulation). And we have two dead cells that have three live neighbours, so these will spring to life in the next generation (as if by reproduction). Once we apply all of these rules, the new generation, Generation 1, will be as shown in Fig.1c. Although the GOL may appear to be simple at first, this is deceptive because there are hidden layers of sophistication. For example, different groupings of live cells can result in very different behaviors from generation to generation. Some patterns are known as ‘Still Lifes’ because they don’t change from one generation to another unless something happens to disturb them. Slightly more complicated 1 3 3 ( a) P hysic al wiring and pix el num bering are the ‘Oscillators,’ which will return to their initial state after some finite number of generations. Things start to get really interesting when we meet patterns that can translate (move) themselves across the universe (grid or array). The first of these patterns, and the simplest, is called the ‘Glider’, which was discovered by Richard Guy in 1970. Consider the Glider shown in Fig.2. After four generations we see the original patten reappear, but it’s moved one cell to the right and one cell down as compared to its original location in Generation 0. If we keep on running, the Glider will continue to make its way across the array. When you first meet the rules for the GOL, you might think of them as being relatively easy to define. In reality, it took Conway and his colleagues a lot of time and mental effort to arrive at an ideal balance that wouldn’t result in the game experiencing rapid underpopulation or overpopulation. The amazing thing to me is that Conway didn’t have access to a computer when he developed these rules. Instead, he used the board and pieces from the game of Go to experiment with alternative scenarios. (If you want to learn more about Conway, one book I really enjoyed (but that may now be out of print) is Genius at Play: The Curious Mind of John Horton Conway by Siobhan Roberts: https://amzn.to/3kSZCT5). Great (ping-pong) balls of life! On the off chance you’ve only just started subscribing to Practical Electronics, I ( b) T he way we’ v e been thinking 1 4 4 1 3 3 should probably make mention of the fact that, since the March 2020 issue, here in the world of Cool Beans (where the flowers are more colourful and smell nicer, the butterflies are bodacious and brighter, and the birds are fluffier and sing sweeter), we’ve been performing experiments with a 12×12 array of ping-pong balls, each equipped with a tricolor LED in the form of a WS2812B (a.k.a. ‘NeoPixel’) device. What we are going to do is to implement a simple GOL on our 12×12 array, but we immediately run into a problem, which is the modest size of our universe (array). Do you recall earlier when we noted that, ‘The universe of the GOL is an infinite, two-dimensional orthogonal grid of square cells’? Well, our universe is only 12×12 cells in size, which some may feel to be a tad less imposing than ‘infinite’. Of course, the ‘infinite’ descriptor is associated with a theoretical GOL. In practice, every GOL creator has to handle a bounded universe by one means or another. In my previous column (PE, April 2021), we discussed three ways in which we could treat the edges of our universe. We called the first option ‘Keep on trucking.’ The idea here is that when something like a Glider in the GOL reaches the edge of the array it disappears from the picture and we all shrug our shoulders and pretend that it’s heading out into the great beyond. We referred to our second option as ‘Bouncing off the edge.’ As this name implies, when something reaches the edge of the array, it bounces back into the body of the array. Now, although this was easy to achieve with the individual pixels discussed in my previous column, it would be a lot harder to implement for something like a GOL Glider. If we were desperate to realise a ‘Bouncing off the edge’ scenario in our GOL simulation, one approach we might try would be to = [0 ,0 ] ( c ) ‘ Opposite D ay’ thinking 0 1 1 1 1 3 2 1 2 1 1 0 9 1 2 0 9 2 1 0 8 9 7 8 3 8 5 9 6 7 4 8 4 7 3 6 1 7 2 6 0 4 9 3 7 4 8 3 3 6 2 5 2 9 1 3 2 4 1 1 0 1 2 R ows ( Y ) R ows ( Y ) 1 0 6 5 4 1 1 1 0 9 8 7 6 5 4 3 2 1 5 6 7 8 1 1 0 1 2 = [0 ,0 ] 0 1 2 3 4 5 6 7 C olum ns ( X ) 8 9 1 0 1 1 0 1 2 3 4 5 6 7 C olum ns ( X ) 8 9 1 0 1 1 Fig.3. Welcome to ‘Opposite Day’ thinking. Practical Electronics | May | 2021 41 0 1 2 3 (Y - 1 ) 4 R ows ( Y ) implement a 1-pixel wide band of imaginary pixels surrounding our array containing virtual cells that are permanently alive (or dead). Our third option was the ‘Curved spacetime’ scenario, in which we imagine the universe (in the form of our ping-pong array) as being curled into a vertical cylinder such that the right-hand side is joined to the left-hand side, while simultaneously being curled into a horizontal cylinder such that the upper side is joined to the lower side. In this scenario, when something like our GOL Glider passes through the right-hand edge of our array, for example, it will reappear on the left-hand side of the array. It’s this ‘Curved spacetime’ scenario that we are going to implement for our GOL. 5 [X ,Y ] 6 Y 7 (Y + 1 ) 8 9 (X – 1 ) 1 0 X (X + 1 ) 1 1 0 1 2 3 4 5 6 7 C olum ns ( X ) 8 9 1 0 1 1 Opposite day Fig.4. If only we didn’t have to take the edges into account. Have you ever watched the American animated comic series SpongeBob SquarePants? In one of the episodes Happy dance or bad-hair day? in Season 1, Squidward tells SpongeBob that it is ‘Opposite Before we plunge into the fray with gusto and abandon, let’s Day,’ which means everyone must act opposite to how they pause for a moment to cogitate, ruminate, and deliberate on usually act. The reason I mention this here is that, as you may what we wish to do. When it comes to evaluating which cells recall from our earlier discussions on the physical construcare going to perform their ‘happy dance’ and which are going tion of the array (PE, August 2020), looking from the front, to have a ‘bad hair day’ in the next generation, one of our the pixels are wired in a serpentine fashion commencing in tasks is going to be determining how many live neighbours the lower right-hand corner and progressing back and forth each cell currently has. until we reach the upper right-hand corner (Fig.3a). Let’s start by playing a simple thought experiment. Let’s In the real world, these pixels are numbered from 1 to 144 assume we are interested only in a single cell referenced by (we also have a ‘sacrificial’ pixel 0 that acts as a voltage level [X,Y] that’s located somewhere in the middle of our array shifter and that isn’t visible from the front of the array). For and we want to determine how many of its neighbours are reasons that made sense at the time, we decided to visualise alive (Fig.4). the array as comprising 12 columns (the X axis) and 12 rows Purely for the sake of this experiment, let’s assume that (the Y axis), both numbered from 0 to 11, with location [X,Y] we’ve declared a two-dimensional (2D) array of cells called = [0,0] appearing in the lower left-hand corner of the array Cells[NUM_Y][NUM_X], where NUM_Y and NUM_X have (Fig.3b). As part of this, we created a GetNeoNum() function both been defined as 12. For reasons that will become apthat accepts X and Y values and returns the corresponding parent, we are going to visualise this array as being presentnumber (iNeo) of the pixel in the array. The core of this funced in the form Cells[Y][X] (as opposed to Cells[X][Y], tion, which we’ve been using in all of our programs since the which would be a tad more intuitive). Let’s also assume that beginning, was as follows (where NUM_XY is defined as 12): these cells can be in one of two states: ALIVE or DEAD. Furthermore, let’s assume that we’ve declared an integer called iNeo = yInd * NUM_XY; numNeigh in which we are going to record the number of neighbouring cells that are alive. If we don’t have to acif ( (yInd % 2) == 0) count for the effects at the edges, we can simply employ dY { // Even row (‘delta Y’) and dX (‘delta X’) values that each range from –1 iNeo = iNeo + (12 - xInd); to +1 as follows: } else numNeigh = 0; { // Odd row iNeo = iNeo + (xInd + 1); for (int dY = -1; dY <= 1; dY++) } for (int dX = -1; dX <=1; dX++) if ( !( (dX == 0) && (dY == 0) ) ) In order to refresh yourself as to how this works, jot down if (Cells[Y + dY][X + dX] == ALIVE) a couple of sample [X,Y] combinations on a piece of paper numNeigh = numNeigh + 1; and then run them through this function as a mental exercise. For example, if we assume [X,Y] = [0,0], then when The way this works is that the nested for() loops take us we apply these values to our function it returns 12, which through every one of the nine cells in the ‘cluster’ centered is the physical number of the pixel in the lower left-hand on the cell at [X,Y]. The first if() test makes sure that we corner. Hurray! aren’t counting the cell at [X,Y] itself. The second if() test Unfortunately, for reasons that will become apparent as we checks to see if the surrounding cell in question is ALIVE proceed, when it comes to our implementation of the GOL, and, if so, increments our count of live neighbours. it’s going to make our lives a lot easier if we think of location Before we proceed, let’s remind ourselves that, in the not[X,Y] = [0,0] as appearing in the upper left-hand corner (Fig.3c). so-distant past, microcontrollers were much less powerful There are a variety of solutions we could employ, but the than they are today. At that time, it was important to coneasiest way to make this happen is to modify our GetNeoserve every clock cycle and/or memory location. In fact, this Num() function so that it returns the values we want to see. may still be the case in some of today’s real-world systems. Why don’t you ponder this for a while to determine what Assuming our goal is to minimise clock cycles, how might changes need to be made (you’ll find my solution at the end we modify our code snippet above? Well, one way would of this column). be as follows: 42 Practical Electronics | May | 2021 if (Cells[Y][X] == ALIVE) numNeigh = -1; else numNeigh = 0; for (int dY = -1; dY <= 1; dY++) for (int dX = -1; dX <=1; dX++) if (Cells[Y + dY][X + dX] == ALIVE) numNeigh = numNeigh + 1; What we’ve done is to take the test that makes sure that we aren’t counting the cell at [X,Y] itself outside of the nested loop in which it used to be performed nine times. Instead, we’ve replaced it with a single test outside the loop that uses the state of this cell to initialise our numNeigh count variable. If cell [X,Y] is alive, we set numNeigh to –1, which means that when we come to count this cell in the nested loop, it cancels itself out, if you see what I mean. Master the modulo Of course, we do need to handle the cells at the edge of the array. Let’s remind ourselves how we dealt with this in our previous column. Purely for the purposes of providing an example, consider a single row of 12 ping-pong balls numbered left-to-right from 0 to 11. Now assume we are looking at pixel 6 (Fig.5). I’m not going to repeat our previous discussions. The important part is that if we are looking at pixel 6, then the pixel to the left will be (Pixel + M1) % NUM_XY, which equates to (6 + 11) % 12 = 17 % 12 = 5. Similarly, the pixel to the right will be (Pixel + P1) % NUM_XY, which equates to (6 + 13) % 12 = 19 % 12 = 7. The important part of all this is that this technique implements our curved spacetime universe. In the case of pixel 11, for example, the pixel to the right will equate to (11 + 13) %12 = 24 %12 = 0. Similarly, in the case of pixel 0, the pixel to the left will equate to (0 + 11) % 12 = 11 % 12 = 11. Once again, I feel a little ‘Tra-la’ wouldn’t go amiss. Unfortunately, there is a slight ‘gotcha’ because we can no longer directly use dX and dY delta values ranging from –1 to +1. On the other hand, this is a small issue in the scheme of things. Assume that we’ve already defined our M1 and PI values as illustrated in Fig.5. Also, that we’ve defined NUM_DELTAS (‘number of delta values’) as being 3. Consider the following snippet of code, which will work for any cell in the array, including the little rascals at the edges: int Deltas[NUM_DELTAS] = {M1, 0, P1}; if (Cells[Y][X] == ALIVE) numNeigh = -1; else numNeigh = 0; for (int dY = 0; dY < NUM_DVALS; dY++) for (int dX = 0; dX < NUM_DVALS; dX++) { int tX = (X + Deltas[dX]) % NUM_XY; int tY = (Y + Deltas[dY]) % NUM_XY; if (Cells[tY][tX] == ALIVE) numNeigh = numNeigh + 1; } I don’t know about you, but when I look at this it makes me think that it’s almost as if we had a plan. Sad to relate, if that’s what you think, then you’d be sadly mistaken because I am literally making this up as we go along. Practical Electronics | May | 2021 N U M _ X Y = 1 2 M A X _ X Y = (N U M _ X Y – 1 ) 0 1 2 ( P ix el + M 1 ) % 3 4 N U M _ X Y 5 P 1 ( “ P lus One” ) = (N U M _ X Y + 1 ) M 1 ( “ M inus One” ) = M A X _ X Y 6 P ix el = 6 7 8 9 ( P ix el + P 1 ) % 1 0 1 1 N U M _ X Y Fig.5. The masterful % modulo operator. One is the loneliest number Do you recall the song ‘One’ (more commonly thought of as ‘One is the loneliest number’), which was originally written and recorded by Harry Nilsson? This song was made famous by the American rock band Three Dog Night, whose recording in the key of F minor reached number five on the US Billboard Hot 100 in 1969. I loathe this song with a passion. The only reason I mention it here is to remind ourselves that all we’ve done so far is to consider the processing of a single hand-picked cell. We’re going to have to scale this up to evaluate every cell in our array. Also, we’re going to have to use this information to determine which cells will live and which will die in the next generation. At this stage it would be a great idea for you to print out the entire sketch (program) so you can follow along (file CB-May21-01.txt, which is available on the May 2021 page of the PE website https://bit.ly/3oouhbl). We won’t go through the entire program here, but let’s take a quick peek at a couple of key elements. First, we declare two enumerated types as shown below (we introduced the concepts of typedef (type definitions), enum (enumerated types), and struct (structures) in Tips and Tricks, PE, December 2020): typedef enum nGenOptions { ALIVE, DEAD }; typedef enum xGenOptions { STAYING_ALIVE, DYING, STAYING_DEAD, COMING_ALIVE }; We also define a structure called CellData and then we declare a two-dimensional (2D) array called Cells[][], where each element of the array is a copy of our structure as follows (nGenState is the state of the cells in the current generation; xGenState is what we are going to use to define the state in the next generation; and numNeigh is the number of live neighbours): typedef struct CellData { nGenOptions nGenState; xGenOptions xGenState; int numNeigh; }; CellData Cells[NUM_Y][NUM_X]; 43 Exciting Freelance Design Engineer Opportunity i n t { We have a product idea that needs engineering input to produce a proof-of-concept prototype. Essentially a Vending Machine dispenser of liquid that can take digital payments, the project will require an individual – or small team – to design a fully operational machine that can demonstrate the core design and key functionality features. The main sub-systems and skills required to achieve success will be: n Robust microcontroller design n Touchscreen user interface n Food-safe operation – in particular, liquid dispensing from a small, cooled storage tank to a customer vessel n Overall systems integration n Web connected n Able to take digital payment – for example, chip and pin, contactless payment and Apple Pay The mechanical engineering side of the project is relatively straightforward and is likely to involve little more than a cooler, a pump, some solenoid valves, a few system monitoring sensors (flow/pressure/temperature) and connecting plumbing. However, understanding and appreciation of the engineering requirements of handling food served to the public is critical. At this stage the design is open-ended, so it is not tied to a particular microcontroller platform. The machine will not be expected to accept traditional cash. If this sounds like your kind of challenge then please email me at: iancrosby668<at>gmail.com If you feel able to complete most but not all of the above then do not be put off, just let me know what you can but also cannot do. Do you remember earlier when we said, ‘We are going to visualise this array as being presented in the form Cells[Y][X] (as opposed to Cells[X][Y], which would be a tad more intuitive).’ Well, this is where we discover the rationale behind this scheme, and also why we decided to think of our array in its ‘Opposite Day’ configuration. One of the things we want to be able to do is load our array with an initial ‘Seed’ configuration. The way I decided to specify this seed is as a 2D array of integers, as shown in Fig.6. Observe that I’ve specified a Glider (shown as red ‘1’ characters) in the lower left-hand corner. You can observe this Glider in action in a video I just captured: https://bit.ly/3bMZbGQ The way the C/C++ compiler works, when we declare a 2D array, the first index is for the rows (our Y axis, with Y = 0 at the top and Y = 11 at the bottom) and the second index is for the columns (our X axis, with X = 0 on the left and X = 11 on the right). Now all becomes clear. When we specify a starting pattern in our Seed[][] array, we want that pattern to be replicated ‘as-is’ on the physical array, otherwise we’re going to end up spending an inordinate amount of time performing mental gymnastics as we try to convert from one representation to another in our heads (and I fear my gymnastic days are behind me). In the same way that there’s no point in barking when you have a dog, there’s also no point making your head hurt performing wacky transformations when you have a computer to do them for you. I think you’ll find the rest of the program easy to understand. As you’ll see, in addition to our GetNeoNum() function, we’ve declared four other functions. The InitializeUniverse() function loads the current generation (Generation 0, in this case) in our Cells[][] array with ALIVE or DEAD values, as defined in our Seed[][] array. The EvaluateNextGeneration() 44 } ; S eed [ N U M_ Y ] [ N U M_ X ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} , { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} / / / / / / / / / / / / / / / / / / / / / / / / / / Y 0 1 2 3 4 5 7 6 8 9 1 0 1 1 | X 0 < - > 0 < - > 0 < - > 0 < - > 0 < - > 0 < - > 0 < - > 0 < - > 0 < - > 0 < - > 0 < - > 0 < - > 1 1 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 Fig.6. An example seed array. function scans the current generation to determine the number of live neighbours for each cell, after which it decides what each cell is going to do going forward to the next generation. If a cell is currently alive, the two choices are for it to stay alive or to die; if the cell is currently dead, then the two options are for it to stay dead or to spring into life. The UpdateCurrentGeneration() function takes the results from the EvaluateNextGeneration() function and uses them to define the next generation; that is, the new current generation. Finally, the DisplayCurrentGeneration() function takes the current generation data and displays it on the physical array. After initialising the NeoPixels by turning them all off, the setup() function calls the InitializeUniverse() function followed by the DisplayCurrentGeneration() function, after which we pause for a couple of seconds to make sure what we see on the physical array is what we expected to see as specified in our Seed[][] array. Next, the loop() function cycles around calling the EvaluateNextGeneration(), UpdateCurrentGeneration(), and DisplayCurrentGeneration() functions. Next time... In our earlier experiments with the array, we faded from one colour to another. Consider the Glider sequence shown in Fig.2. How might we modify our code to fade from one pattern to the next, as opposed to the ‘bang-bang-bang...’ approach we are currently using? Similarly, consider the sequence shown in Fig.1. How might we modify our program to (a) display the intermediate (half-generation) steps, (b) use colours to reflect the four cases of staying alive, staying dead, coming alive, and dying, and (c) to do all this using our fading techniques? In my next column, we will answer these questions, after which we will start to look at a new form of display that – if you are anything like me – will make you squeal in delight. Until then, have a good one! Solution to GetNeoNum() The modifications we need to make to our GetNeoNum() function to reflect our ‘Opposite Day’ view of the world are easypeasy-lemon-squeezy. All we have to do is add the following statement before any of the existing statements (where MAX_XY is defined as being 11): yInd = MAX_XY - yInd; Now, if we assume [X,Y] = [0,0], then when we apply these values to our modified function it returns 133, which is the physical number of the pixel in the upper left-hand corner. Hurray Squared! Cool bean Max Maxfield (Hawaiian shirt, on the right) is emperor of all he surveys at CliveMaxfield.com – the go-to site for the latest and greatest in technological geekdom. Comments or questions? Email Max at: max<at>CliveMaxfield.com Practical Electronics | May | 2021