Silicon ChipTeach-In 2024 - April 2024 SILICON CHIP
  1. Contents: FLOWCODE
  2. Subscriptions
  3. Back Issues
  4. Publisher's Letter: Welcome to the future
  5. Feature: Techno Talk - Wait! What? Really? by Max the Magnificent
  6. Feature: Net Work by Alan Winstanley
  7. Project: Basic RF Signal Generator by Charles Kosina
  8. Project: Swiss Army Knife by Richard Palmer
  9. Project: Silicon Chirp - the pet cricket by John Clarke
  10. Project: Teach-In 2024 by Mike Tooley
  11. Feature: Circuit Surgery by Ian Bell
  12. Feature: Max’s Cool Beans by Max the Magnificent
  13. Project: AUDIO OUT by Jake Rothman
  14. PartShop
  15. Advertising Index: Peak Test Instruments
  16. Back Issues

This is only a preview of the April 2024 issue of Practical Electronics.

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

Articles in this series:
  • (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)
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)
Articles in this series:
  • Teach-In 2024 (April 2024)
  • Teach-In 2024 (May 2024)
  • Teach-In 2024 – Learn electronics with the ESP32 (June 2024)
  • Teach-In 2024 – Learn electronics with the ESP32 (July 2024)
  • Teach-In 2024 – Learn electronics with the ESP32 (August 2024)
  • Teach-In 2024 – Learn electronics with the ESP32 (September 2024)
  • Teach-In 2024 – Learn electronics with the ESP32 (October 2024)
  • Teach-In 2024 – Learn electronics with the ESP32 (November 2024)
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)
Articles in this series:
  • Max’s Cool Beans (April 2024)
  • Max’s Cool Beans (May 2024)
  • Max’s Cool Beans (June 2024)
  • Max’s Cool Beans (July 2024)
  • Max’s Cool Beans (August 2024)
  • Max’s Cool Beans (September 2024)
  • Max’s Cool Beans (October 2024)
  • Max’s Cool Beans (November 2024)
  • Max’s Cool Beans (December 2024)
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)
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)
Teach-In 2024 Learn electronics with the ESP32 by Mike Tooley Part 2 – Digital input and output I n last month’s Part 1 of our Teach-In series we provided an overview of the ESP32 and introduced the development environment. We showed you how to use your PC to monitor the ESP32’s built-in capacitive touch sensors and our practical project involved using the ESP32 in a portable emergency beacon. This month, we will be taking a more detailed look at digital I/O and showing you how to interface buttons and switches as well as how to drive loads such as LEDs, relays and sounders. We will also introduce the Serial Monitor. As part of the Arduino IDE, this handy tool provides a great way of testing your code. The learning objectives for this part are: n Know how to use the Serial Monitor n Know how to configure and use simple digital I/O n Know how to increase the output capability of the ESP32. Using the Serial Monitor When testing even the most basic code you will often find that you need some means of knowing what’s going on. Without having the benefit of a full user interface (such as a local keypad and display) this can be tricky, but the Arduino IDE can come to your rescue with its in-built Serial Monitor. However, before you can begin to use this useful tool you will need to initialise the Serial Monitor within your setup() code like this: void setup() { Serial.begin(9600); // Initialize the Serial Monitor } About Teach-In Our latest Teach-In series is about using the popular ESP32 module as a basis for learning electronics and coding. We will be making no assumptions about your coding ability or your previous experience of electronics. If you know one but not the other, you have come to the right place. On the other hand, if you happen to be a complete newbie there’s no need to worry because the series will take a progressive hands-on approach. There will be plenty of time to build up your knowledge and plenty of opportunity to test things out along the way. We’ve not included too much basic theory because this can be easily found elsewhere, including several of our previous Teach-In series, see: https://bit.ly/pe-ti https://bit.ly/pe-ti-bundle There will be projects and challenges to help you check and develop your understanding of the topics covered each month. To send a message to the Serial Monitor you can use the Serial. print and Serial.println() functions. The only difference between these functions is that the latter adds a newline character at the end of the printed string. Here’s an example: Serial.print(“Pin selected = “); You probably noticed the parameter that we’ve used in the Serial.begin() function. This sets the speed or data rate in bits per second (baud) that will be used for serial communication. It is typically set to either 9600 or 115200, but other baud rates can be selected if required. An optional second argument configures the data, parity and stop bits. The default is eight data bits, no parity and one stop bit. This should work fine with the Arduino IDE and many other applications, but you may need to change it for different external hardware. For example, the following code sets the serial UART to 9600 bits per second with eight data bits, even parity and two stop bits: void setup() { Serial.begin(9600, SERIAL_8E2); // Initialize the Serial Monitor } 40 Fig.2.1. Code for testing the ESP32’s built-in Hall effect sensor. Practical Electronics | April | 2024 the variable selectedPin and then move to the next line we would use: Serial.println(selectedPin); Fig.2.2. Enabling the Serial Monitor from the IDE’s Tools menu. This will print the character string enclosed in the quotes and the next item to be printed will appear immediately after the trailing space. So, if we wanted to add a pin number stored in Listings 2.1 and 2.2 show how the serial monitor can be used both to receive data from, and send data to, the ESP32. The first example (Listing 2.1) makes use of the ESP32’s built-in Hall effect magnetic sensor. Note that having entered or downloaded the code (Fig.2.1) you will need to enable the serial monitor from the Tools menu, as shown in Fig.2.2 and select the baud rate, as shown in Fig.2.3. When the code is executed, you will be rewarded with a list of returned data values, updated every 500ms (see Fig.2.4). The data will vary, although residual values of magnetic field should all be fairly low, but the Hall effect sensor can be tested by placing a small magnet close to the upper surface of the ESP32 module, as shown in Fig.2.5. As the permanent magnet is moved towards the ESP32 there will be a marked increase in detected field strength accompanied by a change in polarity if the direction of the field is reversed, as also shown in Fig.2.5. The second example (Listing 2.2) shows how individual characters can be sent from the PC via the serial link. The data Fig.2.5. Using a small magnet to test the Hall effect magnetic sensor. Reversing the field, as shown in (a) and (b) will result in a reversal of the polarity of the returned data. Fig.2.3. Setting the Serial Monitor’s baud rate. Listing 2.1 Using the serial monitor to display data from the ESP32’s in-built magnetic Hall effect sensor /* Using the serial monitor to display data received from ESP32’s in-built Hall effect magnetic field sensor */ int magField = 0; // Initialise data from sensor void setup() { Serial.begin(115200); // Initialise serial monitor } Fig.2.4. Returned data from ESP32’s built-in Hall effect magnetic sensor. Practical Electronics | April | 2024 void loop() { // The main loop repeats forever magField = hallRead(); // Update sensor data Serial.println(magField); // Display using serial monitor delay(500); // Short delay between readings } 41 Gotcha! During compilation you may receive an error message informing you that ‘hallRead was not declared in this scope’. If that’s the case, it is worth checking the currently installed version of the Espressif System esp32 board library. The failure to compile seems to arise in the current ‘3.0.0 alpha’ version. To put things right you can use the IDE’s Library Manager (see Part 1 last month) to remove the current version before installing a previous version (we used version 2.0.11 without this problem occurring). to be sent (a single character followed by New Line) is entered into the Serial Monitor’s message field, as shown in Fig.2.6. The code will allow you to toggle the ESP32’s in-built LED (on GPIO2) ‘on’ and ‘off’ using a lower or upper case ‘X’ entered from the keyboard. Listing 2.2 Using the serial monitor to control the ESP32’s in-built LED /* Using the serial monitor to toggle the ESP32’s in-built LED on GPIO2 using the ‘x’ keys on the keyboard */ int ledPort = 2; boolean LEDstatus = HIGH; void setup() { pinMode(ledPort, OUTPUT); Serial.begin(115200); } // GPIO2 is connected to the LED // Initialise the LED // Initialise the serial monitor void loop() { char ch = Serial.read(); // Check for a character // Check for a valid keyboard entry if ((ch == ‘X’) || (ch == ‘x’)) { digitalWrite(ledPort, LEDstatus); // Set on or off LEDstatus = !LEDstatus; // Toggle the status } delay(100); // Short delay } Configuring GPIO digital outputs The output state from a GPIO line can be HIGH (approx. 3.3V) or LOW (close to 0V). The GPIO pins can source current when in the HIGH state or sink current when in the LOW state. In both cases (and to keep well within the maximum ratings for the ESP32) we recommend the current that’s sourced or sunk should always be less than 10mA (equivalent to a load of no less than about 330Ω). An output device (or ‘load’) can be connected so that it can be activated by either a HIGH state output or by a LOW output condition (see Fig.2.7). A current-limiting series resistor is required when driving an LED. The value of LED series resistor should normally be within the range 220Ω to 470Ω. For most of our examples we have used 330Ω, which results in a typical LED current of around 4mA. If more brightness is required, the value of series resistor can be reduced to 220Ω. This brings us once again to the important question of just how much current we can safely extract (source or sink) from an Fig.2.8. Circuit of the ESP32 traffic lights controller. Fig.2.6. Entering character data into the Serial Monitor’s message field. Fig.2.7. Output loads activated by (a) HIGH and (b) LOW output states. 42 Fig.2.9. Wiring layout for the ESP32 traffic lights controller. Practical Electronics | April | 2024 ESP32’s GPIO pins. Although the Espressif datasheet states a maximum output drive current of 12mA, we recommend limiting the load current on any individual GPIO line to no more than 10mA, while at the same time ensuring that the total load on the ESP32’s digital outputs is less than 100mA. This should give plenty of scope for most applications, but where additional current drive is required an external transistor can be used, as described later in our Practical Project. Listing 2.3 Code for the ESP32 twoway traffic light controller /* ESP32 traffic lights. Outputs red, amber and green control signals for a 2-way road junction */ // Define GPIO pins const int Red1 = 12; const int Amber1 = 13; const int Green1 = 14; const int Red2 = 18; const int Amber2 = 19; const int Green2 = 21; const int delaytime = 1000; digitalWrite(Amber1, LOW); digitalWrite(Green1, LOW); digitalWrite(Red2, LOW); digitalWrite(Amber2, LOW); digitalWrite(Green2, HIGH); delay(5 * delaytime); // State 3 digitalWrite(Red1, HIGH); digitalWrite(Amber1, LOW); digitalWrite(Green1, LOW); digitalWrite(Red2, LOW); digitalWrite(Amber2, HIGH); digitalWrite(Green2, LOW); delay(delaytime); // State 4 digitalWrite(Red1, HIGH); digitalWrite(Amber1, LOW); digitalWrite(Green1, LOW); digitalWrite(Red2, HIGH); digitalWrite(Amber2, LOW); digitalWrite(Green2, LOW); delay(delaytime); // State 5 digitalWrite(Red1, HIGH); digitalWrite(Amber1, HIGH); digitalWrite(Green1, LOW); digitalWrite(Red2, HIGH); digitalWrite(Amber2, LOW); digitalWrite(Green2, LOW); delay(delaytime); // State 6 digitalWrite(Red1, LOW); digitalWrite(Amber1, LOW); digitalWrite(Green1, HIGH); digitalWrite(Red2, HIGH); digitalWrite(Amber2, LOW); digitalWrite(Green2, LOW); delay(5 * delaytime); // State 7 digitalWrite(Red1, LOW); digitalWrite(Amber1, HIGH); digitalWrite(Green1, LOW); digitalWrite(Red2, HIGH); digitalWrite(Amber2, LOW); digitalWrite(Green2, LOW); delay(delaytime); Check it out! Our Check it out! feature involves building a simple two-way traffic lights controller shown in Fig.2.8. Each of the two traffic void setup() { lights (Light 1 and Light 2) will comprise // All pins configured as outputs a set of three LEDs: red, amber and green. pinMode(Red1, OUTPUT); Rather than use individual LEDs (together pinMode(Amber1, OUTPUT); with series resistors) we’ve taken the easy pinMode(Green1, OUTPUT); option and used a ready-built mini traffic pinMode(Red2, OUTPUT); lights module fitted with red, amber and pinMode(Amber2, OUTPUT); green LEDs, together with series 330Ω pinMode(Green2, OUTPUT); resistors. The module is available from } several online suppliers, and it uses a common ground (GND) connection so void loop() { // State 0 that the individual LEDs will become digitalWrite(Red1, HIGH); illuminated when their associated input digitalWrite(Amber1, LOW); pins are taken HIGH. (Just search for ‘5V digitalWrite(Green1, LOW); Mini Traffic Light LED Display’; eBay and digitalWrite(Red2, HIGH); others offer plenty of choice.) digitalWrite(Amber2, LOW); The wiring layout for the ESP32 digitalWrite(Green2, LOW); traffic lights controller is shown in delay(delaytime); Fig.2.9. The code shown in Listing 2.3 // State 1 should be entered or downloaded from digitalWrite(Red1, HIGH); the April 2024 page of the PE website: digitalWrite(Amber1, LOW); digitalWrite(Green1, LOW); https://bit.ly/pe-downloads digitalWrite(Red2, HIGH); When the code is executed the two digitalWrite(Amber2, HIGH); traffic lights should follow the sequence digitalWrite(Green2, LOW); shown in the state table (Fig.2.10). delay(delaytime); Notice how States 2 and 6 are each to // State 2 be displayed for five seconds while the } digitalWrite(Red1, HIGH); remaining states are displayed for 1 second. These display times are achieved by introducing delays of appropriate length in the code. Note applications include sensing the state of buttons and switches that it is only necessary to modify one line of code to change as well as a host of sensors with digital outputs, such as float the timing of the lights. The code is very straightforward and switches, pressure switches, touch switches, microswitches, should need no further explanation. proximity switches and magnetic reed switches. These devices can all make use of a straightforward connection to a GPIO Configuring digital inputs There are numerous applications in which you might need to make use of one or more of the ESP32’s digital inputs. Such Fig.2.10. Sequence table for the traffic lights controller. Practical Electronics | April | 2024 Fig.2.11. A selection of commonly available buttons and switches ideal for use with the ESP32. 43 Gotcha! GPIO pins 34, 35, 36 and 39 are only available for digital input and do not have internal pull-up and pull-down resistors. So, if you need to use these pins for digital inputs, remember to fit external pull-up or pull-down resistors. input pin. A selection of commonly available buttons and switches is shown in Fig.2.11. Pull-up or pull-down? We need our digital inputs to be in one of two states, either HIGH or LOW with nothing in between. The former is usually defined as a voltage level that’s greater than 75% of the nominal 3.3V supply, while the latter is taken as less than 25% of the same value (these two states correspond to more than 2.5V or less than 0.8V, respectively). Note that, by default, the GPIO inputs adopt a high-impedance state, and this invariably results in an unpredictable and indeterminate input Because of this you will often find that the GPIO pins are either pulled HIGH (pulled up) or pulled LOW (pulled down) by means of external resistors. Values are generally uncritical and can often be in the range 4.7kΩ to 100kΩ. Fig.2.12 illustrates the use of this technique. Gotcha! A problem arises with some types of mechanically actuated switch in which the switching action is not clean, and the output state fluctuates rapidly before settling to a steady value. This condition is referred to as ‘switch bounce’ (see Fig.2.14) and it can be undesirable in many applications. The effects of switch bounce can be reduced or eliminated by various means including the introduction of software delays or by slowing the switching action using a simple capacitor-resistor (C-R) network between the switch contacts and a GPIO input pin, as shown in Fig.2.15. Fig.2.14. Waveform showing switch bounce. It’s important to be aware that the ESP32 has a rather neat feature that allows inputs to be pulled HIGH or LOW without the need for external resistors. This can be achieved during GPIO initialisation using the pinMode() command, as shown in the following fragment of code: pinMode(18, INPUT); D18 is a default digital input pinMode(19, INPUT_PULLUP); D19 is a digital input pulled high pinMode(21, INPUT_PULLDOWN); D21 is a digital input pulled low The effect of INPUT_PULLDOWN and INPUT_PULLUP is illustrated in Fig.2.13. Note that the ESP32’s internal pull-up/ pull-down resistors have a nominal value of 45kΩ. Fig.2.12. External pull-up and pull-down resistors. Fig.2.13. Equivalent internal pull-up and pull-down resistors. 44 Practical project (Part 1) Our Practical Project involves the design, construction and coding for a simple motor controller. The minimal circuit and wiring layout for our prototype motor controller are shown in Fig.2.16 to Fig.2.18 (the pin connections for the LED were shown in Fig.1.21 last month). We’ve used 4.7kΩ external pull-down resistors in conjunction with the RUN and STOP switches and a 330Ω series resistor for the LED (later, we will be replacing this with a small motor and driver). Having connected the circuit, made the USB connection to your PC and started the IDE, you can enter (or download) the code shown in Listing 2.4. When you execute the code, you should see that the LED becomes illuminated when the RUN button is pressed. The LED should then remain illuminated until the STOP button is operated. A subsequent press of the RUN button should restart the LED. Now let’s walk through the code. The first few lines of code define the constants used to allocate the GPIO pin numbers to the two buttons and LED. Next, we define the variables that will represent the states of Fig.2.15. Using a C-R network to the two buttons and the reduce the effects of switch bounce. Practical Electronics | April | 2024 Fig.2.16. Circuit for the prototype motor controller. Fig.2.17. Circuit layout for the prototype motor controller (based on a 30-pin ESP32 board). LED output. Finally, the GPIO pins are then configured in the setup() code. The main loop of the code repeats forever and starts by reading the state of the two buttons. Having read the button states we decide what to do depending upon the current state of each button. If the RUN button has been operated, we activate the LED but if the STOP button has been operated, we turn the LED off. Later, in Coding workshop we will take a close look at how these decisions are made. Having verified that the prototype works we can add a small motor to be controlled by the RUN and STOP buttons. Bearing in mind the current limitation mentioned earlier we will need to add some extra circuitry to supply the greater current required. We will return to this in Part 2 a little later, but first it’s worth considering how to drive loads that demand more current or voltage than the ESP32 can provide. Driving high-current/high-voltage loads The ESP32’s current drive capability is sufficient for a lot of purposes (including illuminating an LED) but not enough to operate actuators, motors, lamps and many other ‘real-world’ output devices. Fortunately, the problem of driving the vast majority of high-voltage and high-current loads can be easily solved using one or more miniature relays. These electromechanical devices comprise a coil wound on a high-permeability core and a moving armature mechanically linked to a set of contacts that make and break when the device is actuated. When sufficient current is applied to the coil of the relay the resulting magnetic field will cause the soft iron armature to pull-in and this in turn will open or close the relay’s electrical contacts. A typical miniature PCB mounted relay will operate from a 5V supply, and its contacts will pull-in at around 75% of this value. The specifications of such a relay are listed in Table 2.1. Solid-state relays (like those shown in Listing 2.4 Simple motor control /* ESP32 button control example. One button will be for Start and the other for Stop. The output will be indicated on an LED*/ // Define constants for the GPIO pins const int runButton = 19; // GPIO19 const int stopButton = 21; // GPIO21 const int ledOutput = 18; // GPIO18 // Define variable states int runButtonState = LOW; int stopButtonState = LOW; int outputState = LOW; // No user input yet so both // buttons will be low // Initially this must be low void setup() { // Initialize the LED pin as an output pinMode(ledOutput, OUTPUT); // Initialize the two push button pins as an inputs pinMode(runButton, INPUT); pinMode(stopButton, INPUT); } Fig.2.18. Wiring for the prototype motor controller (based on a 30-pin ESP32 board). Fig.2.19. Solid-state relays designed for controlling AC mains with maximum load currents ranging from 10A to 45A. Practical Electronics | April | 2024 void loop() { // Read the current state of the two buttons runButtonState = digitalRead(runButton); stopButtonState = digitalRead(stopButton); // Check if the buttons are pressed if (runButtonState == HIGH) { outputState = HIGH; digitalWrite(ledOutput, HIGH); } if (stopButtonState == HIGH) { outputState = LOW; digitalWrite(ledOutput, LOW); } } 45 Parameter + Table 2.1 Electrical specifications of a typical miniature relay Value Nominal operating voltage 5V DC Nominal operating current 73mA Maximum load rating AC 250V 10A, DC 30V 10A Pull-in voltage (typical) 3.8V DC coil resistance 70Ω Power consumption (typ) 0.36W Operating time (max) 10ms Release time (max) 5ms Contact resistance (max) 0.11Ω Operating life 100,000 operations at rated load Maximum switching rate 30 operations per second Fig.2.19) can be used to control much greater AC mains loads of up to 45A, but can still operate from a low voltage input. It is important to note from Table 2.1 that the relay coil requires an operating current that’s well beyond the output drive capability of the ESP32. We therefore need an interface that will provide the extra current required. Fortunately, this can be little more than a low-power transistor and a handful of other components, as we shall see later in Part 2 of our Practical Project. A neater, and often more cost-effective alternative is that of making use of a ready built relay interface board. Luckily, there are quite a few to choose from and the most common types are fitted with two, four or eight relays with each relay having its own driver circuit. Fig.2.20 shows a typical four-channel relay board. The board has a transistor driver and an opto-isolator for each channel. Similar boards can be purchased very cheaply (often less than £5) and so it is invariably more cost effective to purchase one of these boards rather than attempt to build one yourself. Coding relay outputs It’s very easy to control one or more relays using just a few lines of simple code. First, you will need to make sure that you define the digital output pins to which the relays are connected using a line of the form: int pump = 18; int heater = 21; // Pump connected via a relay on GPIO pin-18 // Heater connected via a relay on GPIO pin-21 Next you will need to add a couple of lines into the setup() code block, as follows: Fig.2.21. Circuit of the four-channel relay interface board. pinMode(pump, OUTPUT); // Pump is configured as an output pinMode(heater, OUTPUT); // Heater is configured as an output The relays and their respective loads can be turned on and off incorporating the following lines of code at appropriate points in the main program loop: Fig.2.20. A four-channel relay interface board. 46 digitalWrite(pump, LOW); // Turn the pump off digitalWrite(pump, HIGH); // Turn the pump on digitalWrite(heater, LOW); // Turn the heater off digitalWrite(heater, HIGH); // Turn the pump on Practical Electronics | April | 2024 An example might be counting items into batches of 10 on a conveyor. Let’s assume that we need to operate an LED when the count reaches (or exceeds) ten items. The following fragment of code would do this: if (count >=10) { digitalWrite(fullLED, HIGH); } If the value of count is less than 10 then the condition evaluates false and the statement following the condition is simply ignored. However, if the value of Fig.2.22. Adding a transistor driver and motor to the output of the motor controller. count is 10 or greater then the condition evaluates true and the statement following the condition is Practical project (Part 2) executed. In some applications it can be appropriate to use Now let’s return to the Practical project. We can add a motor to a series of if statements to detect various conditions and to the output of the ESP32 (GPIO pin-2) using a transistor driver act on them accordingly. similar to those used in the four-channel relay interface shown in Fig.2.20. The modified circuit and wiring diagrams for the The if ... else construct motor controller are shown in Figs.2.22 and 2.23 respectively. The if ... else construct is very straightforward. The The motor is a low-cost miniature 5V component fitted with basic syntax is just: an integral gearbox. TR1 provides the larger current drive for the motor (typically 50 to 100mA) and acts as a switch if (conditional expression) { which is open when the GPIO pin-2 goes LOW and is closed // code to be executed if true, when GPIO pin-2 goes HIGH (see Fig.2.24). Diode D1 is used // each statement ending with ; to protect TR1 from the back EMF generated when the motor } else { current flow ceases. Similar diodes are used whenever inductive // code to be executed if false loads (such as relays, solenoids and motors) are used as loads. // each statement ending with ; } Coding workshop Being able to make decisions based on what’s happening and Here’s a simple example. Let’s assume that we are monitoring then acting on this information in different ways is an essential an analogue voltage and wish to set a threshold of 512 as the pre-requisite of any programming language. The C-code used value at which a green LED should become illuminated and, in the Arduino IDE provides you with a variety of different below this value, we would like a red LED to be turned on. Our conditional constructs that allow you to do this. Simple if … else construct would then look something like this: decisions can be made using nothing more than if and else, while loops can be controlled using while, do while, for and if (inVoltage >=128) { loop. We will look at all of these, starting with if and else. digitalWrite(greenLED, HIGH); } else { The if construct digitalWrite(redLED, HIGH); The if construct is the simplest of all the condition constructs. } It is used when a statement (or a series of statements) should be executed when a particular condition prevails. The basic Unfortunately, this isn’t quite the whole story. The red and syntax is as follows: green status LEDs should be mutually exclusive and so we might need to ensure that when one LED is turned on the other if (conditional expression) { LED is turned off. There are various ways that we could do // code to be executed if true, this. We could either set them both off before we arrive at the // each statement ending with ; if else construct or we could turn one on and the other off } Fig.2.23. Wiring of the modified motor controller. Practical Electronics | April | 2024 Fig.2.24. Switching action of the transistor in Fig.2.22. 47 within a construct containing more than one statement (ie, a compound construct). Using the first method we might have: while (limitSwitchStatus == LOW) { // Limit not reached so run the motor digitalWrite(motorRun, HIGH); // Check to see if anything has changed? limitSwitchStatus = digitalRead(limitSwitch); } // start with both LEDs off digitalWrite(redLED, LOW); digitalWrite(greenLED, LOW); // now decide which LED to put on if (inVoltage >=512) { digitalWrite(greenLED, HIGH); } else { digitalWrite(redLED, HIGH); } By making the conditional expression dependent on the value of a counter modified inside the loop we have a simple means of performing one or more statements a predetermined number of times, as follows: The other possibility is: count = 0; while (count < 50) { // code to be executed 50 times // each statement ending with ; count = count+1; } // Read the input voltage and // put the red or green LED on if (inVoltage >=512) { digitalWrite(greenLED, HIGH); digitalWrite(redLED, LOW); } else { digitalWrite(redLED, HIGH); digitalWrite(greenLED, LOW); } Notice how in this example we’ve compounded several statements after the if and else. The curly braces, { and }, help make the logic clear and unambiguous. Of course, at some point earlier in the code we would have to define the pins that we are using to control the two LEDs and initialise the variables (inVoltage, redLED and greenLED). Conditions There are several logical conditions that we can test for, and they are summarised in Table 2.2. Notice how an exclamation mark (!) is used to denote ‘not’. In the previous example you should have noticed the >= condition that we used to find out whether the input voltage has exceeded the threshold value of 512. The ‘greater than or equal to’ condition isn’t the only one that we have to play with, as Table 2.2 shows. The while construct The while construct provides you with a means of continuously executing one or more statements until a condition evaluates false. The loop containing the statement (or statements) will continue to be executed as long as the condition remains true but, as soon as it becomes false the loop will terminate and execution will continue with the next subsequent statement. The basic syntax is: while (conditional expression) { // statements to be executed if true, // each ending with ; } Here’s an example that shows how a belt motor could be controlled using a while loop. The belt motor will run for Table 2.2 Conditions 48 as long as it takes for an item placed on the belt to reach a limit switch. Note that we must check the status of the limit switch inside the loop. If we forget to do this the motor will run forever! In this wait loop we increment the counter on every pass through the loop until it reaches 50, at which point the conditional expression evaluates to false and execution continues with the next statement in the code. Note that there’s a neater way of incrementing the count value, as follows: count = 0; while (count < 50) { // code to be executed 50 times // each statement ending with ; count++; } In this case, count++ is used to ‘post-increment’ the value of count. In other words, it takes the current value of count, adds one to it and places the new value back into the count variable. Finally, here’s an example showing how a simple wait loop could be used to flash an alarm LED ten times: flashCount = 0; while(flashCount < 10) { digitalWrite(alarmLED, HIGH); delay(500); // wait half a second digitalWrite(alarmLED, LOW); delay(500); // wait half a second flashCount++; } The do ... while construct The do ... while loop works in a similar fashion to the while loop but with the exception that the condition is tested at the end of the loop, not the beginning. This means that the statements within the loop will always be executed at least once. The syntax is as follows: Code a == b Meaning Notes a is equal to b True if a and b have the same value a != b a is not equal to b True if a and b have the different values a > b a is greater than b True if a is larger than b (but not true if they have the same value) a < b a is less than b True if a is smaller than b (but not true if they have the same value) a >= b a is greater than or equal to b True if a is larger than b (and also true if they have the same value) a <= b a is less than or equal to b True if a is smaller than b (and also true if they have the same value) Practical Electronics | April | 2024 do { // code to be executed at least once, // each statement ending with ; } while (conditional expression); Here’s an example of reading a pressure sensor and waiting a short time for the output to reach a steady value: do { delay(100); // wait for the value to settle cp = readPressure(); // read the pressure sensor } while (x < 10); In this example we are calling the readPressure() function 10 times before arriving at the final value returned from the sensor. The for loop construct The for loop is widely used in almost every computer language, and C is no exception. The construct is used to repeat a statement (or series of statements) whenever a condition evaluates true. If the condition evaluates false, then the loop is exited, and execution continues with the statement that immediately follows the loop. The for loop must be initialised at the outset and thus is a little more complex than the while loop. The basic syntax is: for (loop initialization, conditional expression, increment) { // statements to be executed if true, // each ending with ; } As with the while loop, a counter is often used to control the loop, and this is incremented or decremented each time round the loop. This makes the construct ideal for use in any repetitive application, for example, checking the status of several I/O lines. It is important to remember that loop initialisation occurs only once and before the loop is executed for the first time. The next code fragment shows how the ASCII character set can be sent to the serial monitor. Note that, for this to run, we would first need to initialise the serial port interface using a line such as Serial.begin(9600). Note also that we have declared the count variable (i) within the loop initialisation itself. for (int i = 0; i <= 63; i++) { testValue = 64 + i; Serial.print(testValue); Serial.print(“\n”); delay(100); } Program structure and layout By now you should have gained some idea of what code looks like and how it is structured, but before we go any further it is well worth explaining the layout in a little more detail. You may have noticed that the first few lines of code in a program usually take the form of a heading enclosed between pairs of characters, /* and */, which constitute a comment block. Everything between these two characters is taken as plain text and, since this has no effect on program execution you can use as many lines of text here as you want. The code’s title comment block is usually followed by several variable declarations. The reason behind this is simply that, in the C language, variables must always be declared before they are used. In fact, declarations don’t have to be placed at the beginning of the program code but the point at which they are declared Practical Electronics | April | 2024 Fig.2.25. Motor controller with toggle action RUN/STOP button. (ie, their position in the program) can impose restrictions on the scope over which they can be used. However, since we often need to use variables on a global basis (ie, anywhere in our program code) we will often place them before any of the other code. Declarations involve assigning a variable type (see last month), a name and (optionally) an initial value. Next, follows code that’s used for setting up. This code is placed in a function called setup() and it is executed at the beginning and only once. The setup() function is often used to specify the pin modes (ie, input or output) and to configure the IDE’s Serial Monitor, but if they are not being used then the setup() function can simply be left empty. The main program code is written inside a loop that executes forever (or until the reset button is pressed or the power is removed). This loop() function contains the functions that will execute when the program is being run. Each function takes the form of a block of code that is executed whenever the function is called. Functions can be the ones that are built into the language, or they can be user-defined. This feature allows us to extend the basic language for our own needs with our userdefined functions calling other functions (both user-defined and in-built) as and when required. Function declarations take the form of one or more statements enclosed between curly braces, { and }. Note that each individual statement must end with a semicolon, ;. As well as the block comments that we mentioned earlier, comments can be placed in-line. These consist of plain text appearing after two // characters and added at the end of the line to which they apply. As previously mentioned, comments provide us with a useful reminder of what’s going on in the code and they can be invaluable when maintaining and debugging a program. Indenting (often by three or four spaces) is used to assist with program readability. This becomes particularly important when functions are enclosed within other functions. Top tip – do note that the IDE can tidy up your code very nicely if you select Tools and then Auto Format. Teach-In challenge Our second Teach-In challenge is one that will test your coding skills. Fig.2.25 shows a simplified version of the motor controller in which only one button is provided. This button is to have a toggle function; press once for RUN and then again for STOP. See if you can modify the code from Listing 2.4 to achieve this. Next month Next month, we will move into the analogue world and investigate the ESP32’s analogue input and output capability. We will be introducing techniques for generating analogue waveforms, and our Practical Project will feature a simple battery checker. Coding workshop will provide an explanation of the binary, octal and hexadecimal number systems. 49