Chapter 9

Arduino audio control

This chapter focusses on the design and construction of a more significant project – an Arduino-controlled audio amplifier with 2-band equalizer. This project combines elements of your learning from previous chapter projects in this book:

1.Chapter 5 – making decisions based on control inputs (i.e. switches and piezoelectric sensors)

2.Chapter 7 – amplifying an audio signal (using the LM386 to drive a loudspeaker as an output actuator)

3.Chapter 8 – filtering an audio signal for amplification (the 2-band equalizer amplifier circuit)

The final system uses digital (push button) and analogue (potentiometer) control inputs to change the state and data of the system; this requires adding nested selection statements to the Arduino code to make more complex decisions. The code will also be extended beyond the serial communication of MIDI to work with the serial peripheral interface (SPI), a standard digital communications protocol for controlling multiple integrated circuits. In this project, the Arduino will act as the controller for two MCP413-103 digital potentiometer chips that respond to control information sent over the SPI bus that connects all three together. In so doing, the digital potentiometers now act as the interface to an audio rendering system that combines the audio amplifier from chapter 7 with the 2-band equalizer from chapter 8. The digital potentiometers will control the level of filter output sent to the amplifier, allowing the Arduino to act as the digital controller for an analogue audio system.

This chapter will take time and patience to complete, as there are areas of new material (such as nested selection statements, digital potentiometers) that will expand your current knowledge. In addition, the layout and build of this much more complex project will require time to complete the steps involved, as the limits of a small breadboard are being reached. In practical circuits, the aim is always to keep costs down and reduce the space taken up by the circuit – this also improves performance due to the reduced signal paths involved. Whilst this book does not go into extensive detail on these topics, it is considered useful to finish an introductory text with a project that challenges the limits of the space (and components) involved as this is arguably a significant part of being a professional audio electronics engineer. It is hoped that this project will not only inspire you to progress your learning further, but also give you a greater understanding of the amazing performance of some commercial audio equipment –in terms of both audio quality and also physical size. In so doing, the aim is to foster a desire to learn more about this complex and expanding area – an area where you may be the one to contribute something new and impressive in the future.

9.1 Final project overview

The final project in this book focusses on a practical combination of the work performed in previous chapters, with the outcome being an Arduino-controlled audio amplifier with 2-band equalizer. To do this, the system in Figure 9.1 will be implemented.

Figure 9.1Final project system diagram. The diagram shows how a digital Arduino system is used to control an audio render system that extends the 2-band equalizer amplifier circuit of chapter 8. The Arduino processes sensor inputs from a switch (to select filter) and potentiometer (to set filter level) and then uses the serial bus to control the digital potentiometers (MCP413) as output. The LEDs indicate which filter is currently selected – based on the state of the system. The digital potentiometers replace the analogue rotary potentiometers used in chapter 8, allowing the filter levels to be set by the Arduino. The filter outputs are combined as inputs to the non-inverting terminal of an LM386 audio amplifier, which drives a loudspeaker as an actuator output from the audio render system.

This system will be built in three stages, to allow the processes and functionality of the digital control system to be introduced prior to combining it with the audio render system derived from the 2-band equalizer amplifier circuit built in the previous chapter. The build stages are as follows:

Stage 1: Arduino state control – this stage implements the push-button switch that controls the state of the system, which is also output to the state LED indicators. The analogue control potentiometer is also connected in stage 1A, to allow the data values for the digital potentiometers to be controlled in the second and third stages of the project.

Stage 2: Arduino digital filter control – the second stage of the project connects the MCP413-103 digital potentiometers that are controlled by the serial peripheral interface (SPI) bus that can be implemented using the Arduino. The stage 2 circuit connects a further two LEDs to test varying the potentiometer output values using the analogue control potentiometer.

Stage 3: Arduino-controlled 2-band equalizer amplifier – the final project stage combines the digital control system implemented in stages 1 and 2 with a modified version of the 2-band equalizer amplifier circuit built in chapter 8. One of the main challenges at this point in the build is fitting all components on a single breadboard, and so the chapter 8 circuit is rearranged slightly to accommodate this (hence the chapter 8 circuit cannot be directly extended in this project build).

The system above mimics the structure of a MIDI system (see chapter 2.4, Figure 2.17), where the Arduino now digitally controls the sound produced by the audio render system – which in this case is a 2-band equalizer amplifier circuit. The switch and potentiometer inputs define the state and data of the system, with LEDs being used as control outputs to indicate the current filter selected. State-based programming is a more advanced way of controlling code, which allows the previous processes and events of the system to be retained for future evaluation. In this project, the current filter being controlled is part of a sequence of three possible states that are tracked by the system (Figure 9.2).

Figure 9.2Final project state diagram. The diagram shows the three system states and the transitions between them (computers count from 0). Every time the switch is pressed, the state counter for the system is incremented – from No Change to Low Pass to High Pass. Once the final state (HPF) has been reached, the state counter will be reset on the next switch press to return to the first state (No Change).

The diagram shows the state transition diagram for the final project system, where the current state of the system allows the code to make decisions on the different type of outputs it will produce. In this case, a single switch will be used to iterate through three possible output states:

0.No-change output – this default state prevents unintentional changes being made to a filter value

1.LPF output – when selected, this state will allow the low-pass filter (LPF) value to be controlled

2.HPF output – when selected, this state will allow the high-pass filter (HPF) value to be controlled

The current state of the system will be indicated by the LEDs connected to the control system. Each LED will light up for a specific filter state (LPF/HPF) and neither will light when the system is in the no-change state. The system state dictates the decisions it will make on outputs, but the data in the system provides the information needed to execute the processes resulting from these decisions. In simple terms, when a filter is selected the value of that filter must be defined – this is the data of the system. This means that for each state the Arduino will read the value of the potentiometer (as a control input) and use this to update the digital potentiometers connected to the audio output system. The state and data from the switch and potentiometer are thus used by the Arduino to control the audio render system, which is an amended version of the 2-band equalizer amplifier circuit built in chapter 8. By replacing the analogue potentiometers used to control the filter levels in the chapter 8 project circuit, digitally controlled potentiometers allow the Arduino to directly set the resistance levels of the filters (Figure 9.3).

Figure 9.3Controlling audio filters with digital potentiometers. The left-hand schematic shows a low-pass filter connected to an analogue potentiometer that controls the output level of the filter (Vout). In the right-hand schematic, a digital potentiometer (MCP413) is now used to set the resistance across the output (Vout). The MCP413 can be directly controlled by the Arduino to dynamically update the potentiometer value.

The schematic shows how a digital potentiometer can replace an analogue rotary potentiometer as a control point for an audio filter circuit. The Microchip Technologies MCP413 digital potentiometer can be updated using a serial peripheral interface (SPI), allowing the Arduino to send information to one (or more) of these chips on a single data bus. The following sections of this chapter will cover the stages needed to build an Arduino-controlled audio amplifier with 2-band equalizer. The final circuit layout for this project will split the breadboard into two halves – the top half for the control system, the bottom half for audio render system (Figure 9.4).

Figure 9.4Final project breadboard layout areas. The diagram shows how the final project breadboard will split the components between the control system (top) and the audio render system (bottom). Note the labelling of the power and ground rails (J top, A bottom) – this will be used in all project build stages. In the final circuit, the centre column break will be used to mount the digital potentiometers (MCP413) and audio amplifier (LM386) ICs. Also note that the bottom power rails have been swapped around – this will allow components to be placed more easily in the final project stage.

The diagram shows how the upper half of the breadboard will be used for the Arduino control system, whilst the bottom half will be used for the audio render system. This layout approach aims to keep both elements of the project clearly separated where possible, which is particularly important in a project where layout space will be limited. A single breadboard will be used in the final project, which requires several workaround techniques to be used to fit all components onto a single board – the bottom power rails are swapped in the final build stage for this reason. These will be noted at each stage of the final project build, highlighting the need to carefully follow all instructions step by step throughout this chapter.

9.1.1 Project components

For the completed final project, you will need various components for each stage of the build. The project steps in each of the three build stages assume that the components from each previous stage are retained, as the circuit will be extended in each of these stages to add additional functionality. Some components can be swapped for others you may have available – these include:

Breadboard – the final project build has been designed around a standard 30-row, 10-column (excluding power rails) breadboard, which Tinkercad defines as a small size. The final project layout is designed to work within the constraints of a small 10-column board, primarily because this is the cheapest breadboard to buy and also because the circuits in chapters 18 can easily fit within it. The final project build reaches the limits of such a small board, and so several workarounds for component layout will be used during the build as a result (such as swapping the bottom power rails). In actual fact, this is considered to be a useful constraint as any practical component layout will take up a considerable amount of design time and can lead to some very creative solutions to the eternal problem of reduced space. Although there may be other (perhaps neater!) solutions to the layout that implements the final project schematic, it is important to understand that practical electronics circuits are heavily constrained by both space and proximity (to reduce noise) – this build aims to introduce these elements as a sidebar to the main aim of keeping build costs to a minimum.

LED resistors – the 150Ω current-limiting resistor used for the LED state indicators (and two others in the MCP413-103 output testing in stage 2) can be swapped for 220Ω resistors as used in the chapter 5 MIDI drum trigger project

Analogue potentiometer – the final project images show a 3-pin breadboard mountable potentiometer, which has been used to reduce the number of wire connectors running across the breadboard (as they make other components harder to see). Any other three-connector potentiometer (i.e. Vin, Vout, GND) can be used instead – such as those used in chapters 7 and 8.

Filter components – the audio filters in stage 3 of the build use the same components as the 2-band equalizer amplifier in chapter 8 (16kΩ, 160kΩ, 10nF). If these specific values are not available, other combinations can be used – as long as they are added in an RC (low-pass) and CR (high-pass) configuration for the different filter inputs. If other values are used, equation 8.7 from chapter 8 can be used to determine the Cutoff Frequency, fc=12πRC

Zobel network components – the Zobel network included in both the chapter 7 and 8 project builds is a standard configuration taken directly from the LM386 datasheet. Although analysis of Zobel networks is outwith the scope of this book, any combination of resistor/capacitor components that are around values of 10Ω and 0.05μF can be used.

Power-decoupling capacitors – capacitor values of 1μF and 0.1μF have been used in chapter 7 and 8 to decouple the breadboard power rails (to reduce noise), and in the final project one reason for splitting the board between the control and audio render systems is to reduce the amount of power-supply noise introduced between the input and output audio ground connectors. Additional capacitors added across the power rails will noticeably reduce noise in the circuit, but if values of 1μF and 0.1μF are not available then others in the same range can be substituted.

Final project component list (A full schematic and component list are also included in the Appendices)

1.1 × Arduino Uno R3 (other boards may not conform to the SPI bus implementation used in this project)

2.1 × small breadboard (30 row, 10 column)

3.1 × push-button switch (SW1)

4.2 × LEDs (plus an additional two for the MCP413-103 output test circuit in stage 2)

5.1 × 10kΩ analogue potentiometer (VR1) – a 3-pin breadboard-mountable pot from RS is used

6.1 × 10kΩ pull-down resistor – for the push-button switch (R1)

7.1 × 150Ω current-limiting resistor (R2) – for LED state indicators (plus two additional for stage 2 test circuit)

8.2 × MCP413-103 10kΩ digital potentiometers

9.Audio filter components – low-pass 160kΩ resistor (R3), high-pass resistor 16kΩ (R4), C1 = C2 = 10nF

10.1 × LM386 operational amplifier

11.Zobel network – 10Ω resistor (R5), 0.05μF capacitor (C3)

12.DC blocking capacitor (LM386 output) – 250μF capacitor (C4)

13.Power-decoupling capacitors – 1μF capacitor (C5) and 0.1μF capacitor (C6)

14.1 × audio input connector (3.5mm jack), 1 × audio output connector (loudspeaker)

15.12 × connector cables (for Arduino) and 25 × connector wires – connector cables can be used if wires are not available, but this will significantly increase the visual complexity of the final layout

16.2 × small cable ties – these are usually shipped with electronic cables such as power supplies and headphones, and whilst often consigned straight to the rubbish bin by the consumer are very useful to electronics engineers seeking to keep cables grouped in a larger circuit! The control system uses a tie to group the push-button switch input and LED state indicator output cables together (project stage 1), and a second tie to group the SPI bus connectors for CS¯, SCK and SDI/SDO output pins. This may seem like a trivial addition, but if the circuit does not function correctly and troubleshooting begins then a jumble of connector wires can quickly become a problem in itself.

Figure 9.5Final project components. The image shows (clockwise from top left) the connectors, wires, push-button switch, LEDs, analogue potentiometer, resistors, digital potentiometer chips (MCP413-103), capacitors and the LM386 audio amplifier chip used in the final project build. If sufficient space is available, it can be useful to set out these components near to the breadboard to avoid the confusion of searching for a specific resistor/capacitor value when trying to place components correctly. Having said this, the arrangement above is used to group the project components in a single image – this level of proximity would easily lead to components being mislaid or scattered when building each stage of the project.

The layout in Figure 9.5 shows the full component list for the final project, where the significant amount of connectors and wires required is evident from the image. Most of these connectors will be used to implement the Arduino control system that includes the MCP413-103 digital potentiometers – these chips are the interface to the audio render system, which is broadly the same as the 2-band equalizer amplifier circuit built in chapter 8. It is recommended to source all of the components (and connectors) required prior to commencing the build, to avoid the confusion introduced by searching for them when also focussing on placing those components correctly on the breadboard.

Electronic breadboard layouts can quickly become complex and tiring to work with (particularly when a mistake has occurred), so it is important to spend a little time preparing your components and working area in advance to avoid displacing resistors and capacitors when placing other components on the board – long sleeves and long hair are particularly prone to snagging small electrical wires. This project will require time and patience to work through this much more complex breadboard circuit (where space becomes very limited). It is recommended that you do not rush ahead in this chapter, as many of the elements you have encountered in previous chapters will require small but specific changes in order to work within a larger digital control system. This familiarity can lead to mistakes, so following all processes and steps in the correct order is crucial to building the final circuit.

9.2 Arduino state control

The first section of this chapter will focus on the Arduino control system, combining control inputs for state and data with LED outputs for user interaction (Figure 9.6).

Figure 9.6Final project stage 1 – Arduino control system diagram. The diagram shows the digital control system used in the final project, which is centred on the Arduino microcontroller. Control inputs from a push-button switch and analogue potentiometer will be combined to define the state and data of the system. In this first stage, LED outputs will be used to indicate the state of the system – alongside serial data output of the potentiometer wiper values in the simulator.

The diagram in Figure 9.6 shows the digital control system of the final project, which will be used in later stages to control the audio render system. In this section, input from a push-button switch will be used to iterate through the states of the system (0–2), whilst an analogue potentiometer will provide the data values that the system will use in the next section to set the digital potentiometers. Two LEDs are used to indicate the current state of the system, which will be accompanied by serial output of the data values taken from the analogue potentiometer through the Tinkercad simulator. This is the only stage of the project build that can be simulated in Tinkercad, though it will be used for component layout diagrams for clarity (and consistency with previous projects).

Chapters 4 and 5 introduced the Arduino as a digital microcontroller, where chapter 5 showed how inputs can be processed by code instructions (written in C) to produce outputs. In the MIDI drum trigger project in chapter 5, an onset/offset detector was used to threshold the input from a piezoelectric sensor to determine when to send a MIDI Note On message representing a drum stroke. In addition to thresholding, the code also used a variable called noteTriggered to retain information about the last note event – the previous state of the system (Figure 9.7).

Figure 9.7Onset/offset detection states (adapted from chapter 5, Figure 5.37). The graph shows how a MIDI Note On can only trigger when the input is above threshold and noteTriggered = false. Thus, the state of the system is defined by the variable noteTriggered, which retains the previous event to allow the code to evaluate whether another MIDI Note On has already been sent.

The noteTriggered variable is crucial to the correct operation of the drum trigger, as it prevents multiple MIDI Note On messages being sent without a corresponding MIDI Note Off. In programming terms, noteTriggered is called a state variable because it retains information about the previous state of the system (what MIDI Note was last sent). State variables are used throughout programming to hold information about prior events and processes, and in the final project a state variable will be used to remember what output state the system is currently in.

In the chapter 5 MIDI switch controller project (section 5.8) two input switches were used, where each switch defined a single output (with else being the third Note Off output). This made the logic of the selection statement easier, but also required more components and breadboard space than will be available in this larger final project. For this reason, in the final project a single switch will be used to iterate through multiple states. In this way, one digital switch can be used to execute different Arduino processes by incrementing through each state whenever the switch is pressed. The Arduino will be used to control two digital potentiometers linked to a low-pass and high-pass filter. This means that there will be a total of three states in the system (Figure 9.8).

Figure 9.8Final project system output states. The diagram shows how the system requires a total of three states, relating to both the two audio filters (low and high pass) and also the default state of no change. For each state, the system should be able to control the relevant filter (LPF/HPF) or do nothing – this corresponds to the else condition in a selection statement. LEDs are also used to indicate the current state of the system as a control output.

The diagram shows the overall workflow of the control system in the final project, where a single switch will iterate through three possible states. The No Change (default) state effectively does nothing, but this also prevents unintentional changes and so is crucial to the operation of the system. The LPF and HPF states will read the input potentiometer control value and use this to update a specific MCP413 digital potentiometer. In both cases, an output LED will also light as an indicator of the current filter being controlled to help translate the system states into practical use.

To build multiple states around a single switch input, a selection statement must be amended to include a counter variable that keeps track of the current state. Although a thorough discussion of state variables (and their associated logic) is outwith the scope of this book, a brief explanation of how Arduino code can use a selection statement to retain state information will be given in the practical context of the control system for the final project. Taking a selection statement as an initial template, a counter variable can be added to keep track of how many times the selection statement has executed (Figure 9.9).

Figure 9.9Adding a counter to a selection statement. In the flow diagram, the selection will execute to evaluate the condition of the variable buttonInput. When buttonInput is true (i.e. the switch has been pressed) the stateCounter variable is incremented (stateCounter += 1) to keep track of how many times the button has been pressed – if buttonInput is false, stateCounter is not incremented.

The flow diagram shows how an additional stateCounter variable is used to keep track of how many times the switch has been pressed (see below for more information on incrementing with +=). This is a widely used programming technique, where the previous behaviour of the system is tracked by data to retain information about processes that have already been executed. For the final project, there are three states that must be counted by the code – after which the counter must then reset to return to the first state:

The code shows the outer selection statement for an input variable buttonInput, where the true condition (when the switch is pressed) now contains a nested if statement. This nested selection statement evaluates whether the variable stateCounter has reached 2 (stateCounter < 2) and if not then it increments the variable (stateCounter += 1). As stateCounter is incremented, the else condition of the nested selection statement will occur when (stateCounter == 2) and this resets the counter (stateCounter = 0). In this manner, each time the switch is pressed the counter will increment from 0 to 2 and then reset back to 0 – thus tracking three unique states with the variable stateCounter. The outer else statement only occurs when the switch has not been pressed, and in this instance nothing should happen to change the state of system.

It can often be confusing to remember that a no output state (in the final system this is the default No Change state) is not the same as no input – underlining one of the reasons why the else condition is so important in computer programming.

A nested selection statement can increment a state variable using a single switch, allowing the code to use this variable to make more informed decisions about which processes to carry out. With a control input that changes state, outputs can be added to indicate the current state of the system. As noted with the use of a single input switch, to save on breadboard space two LEDs can be connected in parallel to indicate all three states of the system (Figure 9.10).

Figure 9.10Swapping output polarity to change LEDs. The diagram shows how two LEDs can be connected in opposite directions in parallel, allowing each to be lit separately by the same connecting wires. The first case (No Change state) is indicated by no LED, whilst the second (LPF) is indicated by LED 1 and the third (HPF) by LED 2. To achieve this, the polarity of the Arduino output pins 2 and 3 is reversed to light each LED separately.

The schematic examples show how reversing the polarity of the Arduino output pins (LOW/HIGH) can be used to light each LED separately, where the LOW pin acts as a GND connection for current to flow into (from +5V HIGH). An LED will only allow current to flow in a single direction, so if two LEDs are connected in parallel in opposite directions then only one of them will light when current flows. The Arduino digital output pins can be used to light the LEDs and indicate all three states:

0.No output – pin 2 (LOW), pin 3 (LOW)

1.LPF output – pin 2 (HIGH), pin 3 (LOW)

2.HPF output – pin 2 (LOW), pin 3 (HIGH)

At this point, a Tinkercad project can be built to test this input/output control system.

9.2.1 Worked example – Arduino state control

The system will require a push-button switch for input, and two LEDs connected in opposite directions in parallel for output (Figure 9.11).

Figure 9.11Arduino state control test circuit. The schematic shows how a single push-button switch (SW1) can be connected as an Arduino digital input, with two LEDs (D1, D2) as output indicators to show the state of the system. The polarity of the output pins (2 and 3) will be reversed in the Arduino code to light each LED separately.

The schematic in Figure 9.11 shows a single switch input and two LEDs connected in parallel as outputs, which allows the setup code for the project to be defined:

This code declares the input (buttonInputPin) and output (lowPassLED, highPassLED) pin numbers, alongside an integer to hold the buttonInput (the Arduino API specifies an integer for digitalRead()). The state counter variable (stateCounter) will only increment between 0 and 2, so a byte provides more than enough memory for this value range. The final variable is a Boolean (pushedButton), which is included to prevent multiple triggering on the push=button input (Figure 9.12).

Figure 9.12Multiple switch input triggering. The diagram shows how an input switch going from LOW to HIGH can trigger for multiple cycles of the Arduino loop() function (short vertical lines). The Boolean pushedButton can be used to separate out the first HIGH event (Switch On) and first LOW event (Switch Off) to prevent multiple triggering – note how pushedButton overlaps LOW and HIGH button events.

In this instance, the clock speed of the Arduino means that the loop() function (where the value of pushedButton will be read) will execute at far too high a rate to leave time for any switch to transition from on back to off, leading to multiple cycles of loop() detecting the same push-button input event as a new input. To avoid this, the Boolean pushedButton variable acts in a similar manner to the noteTriggered variable to prevent multiple switch inputs from occurring:

This selection statement tests a combination of two Boolean variables, where the first condition of interest is (buttonInput == HIGH && !pushedButton). The use of !pushedButton occurs when the button has initially been pushed for the first time, and the first instruction inside this condition must then set (pushedButton = true;) to prevent further triggering if the button is still HIGH when the next loop() cycle executes. Thus, the only time a valid button push occurs is when the input is HIGH and pushedButton is LOW.

The second condition of interest is when (buttonInput == LOW && pushedButton). In this case, the value of buttonInput has returned to LOW as the switch is now released, but again only the first one of these events is of interest – when (pushedButton == true). Now, the instruction inside this condition sets (pushedButton = false;) to prevent further triggering of the selection statement. This template condition can now be expanded to include the previous nested condition that increments the state counter:

Now that the input switch control is incrementing the current state through stateCounter, this state variable can be used inside a function call to update the LED output pins accordingly:

The function uses a selection statement to evaluate the current state and update the digital output pins that are connected to the LEDs accordingly. This function can then be called inside loop() after the nested selection statement that increments the state variable, to use the updated value to change the configuration of the LED output pins (2 and 3). This code listing can now be tested in Tinkercad, to ensure that it is functioning correctly. To do this, a prototype circuit for the schematic in Figure 9.11 can be built (Figure 9.13).

Figure 9.13Arduino state control test circuit layout. The diagram shows a push-button switch input connected in series with a 10kΩ resistor to digital pin 4 on the Arduino. Digital pins 2 and 3 are configured for output, with each pin connected to either side of a parallel diode network (in series with a 150Ω resistor that limits current). As the output pins change polarity from LOW (0V) to HIGH (5V) so will each oppositely wired LED light in turn.

The breadboard layout shown in Figure 9.13 is fairly straightforward, other than the placement of both diodes (in opposing directions) in parallel on pins [g9–g10] and [h9–h10] respectively. Although this looks a little strange in the Tinkercad simulator, in practice both LEDs will easily sit on the same columns with adequate space between them. This configuration is intended to save space on the larger project circuit, which also accounts for the connection of the 10kΩ pull-down resistor directly to the GND rail of the breadboard. Create a new circuit named finalProj_stateControl and place the components as follows:

1.Add a push-button switch between pins [i2–f4].

2.Add a 10kΩ resistor between the switch and top ground (JGND) between pins [j2–JGND].

3.Add a connector to the top positive rail [j4–JV+].

4.Add an input connector from the switch to Arduino digital pin 4 [f2–AD4].

5.Add a 150Ω resistor between pins [i5–i9].

6.Add an LED cathode-anode [h9–h10].

7.Add an LED anode-cathode [g9–g10].

8.Add an output connector from Arduino digital pin 2 to the 150Ω resistor [AD2–j5].

9.Add an output connector from Arduino digital pin 3 to the LEDs [AD3–j10].

10.Add a connector between the negative (ground) rail and the Arduino top left GND pin.

11.Add a connector between the positive rail and the bottom Arduino +5V pin.

At this point, the full code listing for the Arduino state control project can now be entered into Tinkercad:

If the code compiles correctly the circuit should run in the simulator, allowing the push button to iterate through the states from 0 to 2 and then back to 0. The LEDs should light in sequence, with the No Change state being indicated when both are off. This stage of the project can now be built on breadboard, with the circuit being retained for later stages to be added to produce the final project build. The reference for top positive (J+) and top ground (JGND) should be noted when placing components.

Stage 1 – project steps

For Stage 1 of the project, you will need:

1. 1 × push-button switch (SW1)

2. 2 × LEDs (D1, D2) for state indicator output

3. 1 × 10kΩ pull-down resistor (R1)

4. 1 × 150Ω current-limiting resistor (R2)

5. 3 × connector cables, 1 × connector wire and 1 small cable tie

1. Add the push-button switch from pins [g2–i4] – ensuring the connectors are aligned A(j4) and B(i4) (see chapter 2, Figure 2.20).

2. Add the 10kΩ pull-down resistor (R1) directly to the top ground rail (to save space) between pins [j2–JGND].

3. Add a connector wire between switch pin A (j4) and the top positive rail [j4–JV+]

1. Add a signal wire from the switch output D(f2) to the Arduino digital pin 4 [f2–AD4].

1. Add a 150Ω current-limiting resistor (R2) between pins [i5–i9].

1. Add the first state indicator LED (D1) between pins [h9–h10] – connect the cathode (h9) and anode (h10).

1. Add the second state indicator LED (D2) between pins [g9–g10] – this time reverse the polarity to connect the anode (g9) and cathode (g10).

1. Add a connector wire from Arduino digital output pin 2 to breadboard pin j10 [AD2–j10].

2. Add a connector wire from Arduino digital output pin 3 to breadboard pin j5 [AD3–j5].

1. Add a connector between the Arduino GND terminal (pin 14) and the breadboard ground rail.

2. Add a connector between the Arduino +5V terminal and the breadboard positive rail (not shown).

3. Use a small cable tie to group these cables together

Copy and paste the code from the finalProj_stateControl project in Tinkercad into the Arduino IDE and save it as a new Arduino project named finalProj_Stage1-Control.ino. Compile the code to check for copy errors and then upload the sketch to the Arduino Uno and test the circuit. If everything compiles correctly, each time the push-button switch is pressed the LED state indicators should increment from No Change (no LEDs), through LPF (LED 1) to HPF (LED 2):

This project has built a control system that uses a push-button switch input to increment a system state variable that will be used in later stages of the project to determine which audio filter will be changed. With the state of the system defined, this circuit can now be extended to include an analogue potentiometer that will set the digital potentiometer data values in the final circuit. This allows a single analogue input potentiometer to act as a control input to the Arduino, which will read the value of the analogue potentiometer and use this to set the digital potentiometers connected to the low-pass and high-pass filters. The input potentiometer will be placed on the top right of the board in our existing Tinkercad circuit to allow other components to be added in the final layout (Figure 9.14).

Figure 9.14Arduino state control test circuit schematic with input potentiometer control. In the diagram, a potentiometer (VR1) has been added as a control input on analogue input pin A0. By combining the potentiometer reading with the state variable (set by the push-button switch) each digital potentiometer can be individually configured by the Arduino.

The diagram shows the addition of a potentiometer as an analogue control input (on pin A0), which can then be used by the Arduino to set the value of each digital potentiometer. The Arduino will now require additional code to be added to the loop() function to respond to changes to the potentiometer input. The potentiometer control input has two criteria that can be framed as questions (to form selection statements) to determine when other processes should execute:

1.Has the potentiometer value changed? – there is no point in executing any other code if the wiper has not been moved by the user, as the data has not changed

2.Does the current state require a potentiometer value? – only the LPF and HPF states require potentiometer input values, so the state of the system must be greater than 0

These two criteria can be combined within a second nested selection statement that tests for both potentiometer input and also system state (Figure 9.15).

Figure 9.15Nested selection statement flow diagram. The diagram shows a nested selection statement where the outer selection evaluates whether the potentiometer control input value has changed – if so then this data value is updated. The inner selection evaluates whether the current state is greater than zero (i.e. a filter has been selected) – if so then the relevant digital potentiometer can be updated.

The outer selection statement in this diagram evaluates the potentiometer control input – if this has not changed since the last cycle of loop() then there is no need to execute any further code. If this value has changed, then the data variable that holds the potentiometer reading must be updated. The inner selection evaluates the current state of the system, to determine whether a filter is currently selected (i.e. state > 0). If a filter is active, the potentiometer control variable can then be used to update the relevant digital potentiometer connected to that filter (see next section). For now, the code to execute this nested selection statement can be added to loop():

The outer selection in this code evaluates whether the potentiometer input value has changed (currentPotValue != potInputValue) – if this is not the case then the selection ends. If the potentiometer has changed, the new value is updated (currentPotValue = potInputValue;) and the current system state is then evaluated in the inner selection statement (lastButtonState > 0). If a filter is selected (i.e. not state 0) then the digital potentiometers can be updated using the function call updateDigitalPot(), which uses the system state variable to determine what output is required:

The updateDigitalPot() function uses output to the Serial Monitor (Serial.println) to check that the logic of the code is working correctly. This code can now be tested in Tinkercad, using the updated layout shown in Figure 9.16.

Figure 9.16Arduino state control with input potentiometer control layout. The diagram shows a +5V signal connected to a potentiometer, where this voltage is split by the potentiometer wiper as an input to Arduino analogue input pin A0. The value of the potentiometer can then be read as the data of the Arduino control system, which will be used to control the digital potentiometers in the final project. Note the positioning of the potentiometer at the top right of the board – this is to allow other control system components to be added in the following sections.

To extend the previous state control circuit, save the file as finalProj_ stateControlPotInput and place the components as follows:

1.Add a 10kΩ potentiometer between pins [i28–i30] (rotate component 180° to keep the pin connections visible).

2.Add a connector to the top ground rail [j30–JGND].

3.Add a connector to the top positive rail [j28–JV+].

4.Add a control input connector from the potentiometer to the Arduino [j29–ADA0].

The full code listing for this system build can then be copied and pasted into the Tinkercad code window:

Stage 1A – Project Steps

For Stage 1A of the project, you will need:

1. Existing stage 1 circuit

2. 1 × analogue potentiometer (VR1)

3. 1 × connector cables and 2 × connector wires

1. Add a connector cable from the top ground to the analogue potentiometer between pins [j30–JGND].

2. Add a connector cable from the top positive rail to the Vin terminal of the potentiometer between pins [j28–JV+].

1. Connect the three potentiometer terminals across pins [i30, i29, i28] – this is easier if using a wired potentiometer, but will add more connectors to the final circuit.

2. Add a connector between the analogue potentiometer wiper and Arduino analogue input A0 between pins [h29–ADA0].

Copy and paste the code from the finalProj_ stateControlPotInput project in Tinkercad into the Arduino IDE and save it as a new Arduino project named finalProj_Stage1-ControlPot.ino. Compile the code to check for copy errors and then upload the sketch to the Arduino Uno and test the circuit. If the code compiles correctly, the circuit should simulate output when a filter is selected (i.e. the system is not in state 0). Each time the LPF (LED 1) or HPF (LED 2) states are selected with the push button, the analogue potentiometer value should be read by the Arduino and output to the serial monitor. The serial monitor statements should show the selected filter and the current value (Figure 9.17).

Figure 9.17Arduino input potentiometer control serial output. The diagram shows the serial monitor output based on push-button switch input (to control state) and potentiometer input (to set the data value for the system). The trace shows how the potentiometer is only updated when changed, and the value will only be used for the relevant filter selected. This allows the system to be connected to digital potentiometers to control their resistance value using SPI communication.

The control system prototyped in Tinkercad should now be capable of changing state (using the push-button switch) and changing the system data variable (potentiometer value). At this point, the first stage of the Arduino control system is now functional. In the next section, this control system will be connected to two MCP413 digital potentiometers to implement the next part of the build.

9.3 Arduino digital filter control

Now that the Arduino control system is functional, it can be extended to include control of the MCP413 digital potentiometers that will be connected to the output of the audio filters (Figure 9.18).

Figure 9.18Final project stage 2 – digital potentiometer control system diagram. The diagram shows how the Arduino digital control system can now be extended to control the MCP413 digital potentiometers that will be used in the audio render system. The MCP413 chips can be controlled using the serial peripheral interface (SPI), indicated by the three connecting wires from the Arduino that branch across the digital control and audio render systems. An LED test output circuit is also shown, which will be used in this stage to verify the correct control and operation of the MCP413 chips.

The diagram shows how the second project stage extends the Arduino control system to include connections to two MCP413 digital potentiometers. Although there are other ways to digitally control audio signals such as voltage-controlled amplifiers (VCAs), digital potentiometers have been chosen for this book primarily because they are relatively inexpensive to buy. In addition, the MCP413 is controlled via the serial peripheral interface (SPI), which is a useful protocol that works well with the Arduino, allowing prior work on serial data output (in chapter 5) to be extended for this project. Microchip Technologies provide a range of digital potentiometers that vary in complexity (and number of potentiometers), but for this project the MCP413-103 chip will be used (Figure 9.19).

Figure 9.19The MCP413-103 10kΩ digital potentiometer chip. The left-hand diagram shows the pin layout for the 8-pin MCP413-103 IC, where pin 1 denotes the chip select (CS) pin that is used to enable communication with the chip. The clock (SCK) and serial data input/output (SDI/SDO) pins are used to receive SPI data, with ground (Vss) and power (Vdd) pins being able to run from +5V Arduino supply. The middle diagram shows how the right-hand pins of the chip operate in the same manner as an analogue potentiometer (right-hand diagram for reference), where P0A typically connects to the input signal, P0W provides the wiper output and P0B is connected to the circuit ground (GND).

The Microchip Technologies MCP413-103 digital potentiometer is a very versatile chip and is relatively easy to work with once the distinction between digital control inputs and potentiometer connections has been made. The left-hand diagram shows the pin layout for the 8-pin IC package, where a broad distinction can be made between control inputs (left pins 1–3) and resistor outputs (right pins 5–7) alongside the power (VDD pin 8) and ground (VSS pin 4) pins. The middle diagram shows the schematic symbol used in this book, which aims to mimic an analogue potentiometer (shown in the right-hand diagram for reference) with additional control inputs connected to the top of the chip.

The chip is digitally controlled to select a specific resistance level from an internal ladder network of resistors, where the data value received on the SDI/SDO pin (0–128) closes one of a series of internal switches that corresponds to the resistance required (Figure 9.20).

Figure 9.20MCP413-103 internal operation. The diagram shows the internal function of a digital potentiometer, which consists of a ladder network of 128 resistors connected to a series of digital switches. The serial data input (SDI) defines the specific switch to close (0–127), which then connects all the resistors in the ladder up to that point to the output P0W. In this way, the P0A input is connected to the P0W output through the combined resistance of the ladder, which varies based on the SDI data.

The diagram shows how the MCP413-103 varies the resistance between input (P0A) and output (P0W) based on an 8-bit binary data value (0–127) received by the chip on the SDI pin. The MCP413-103 is updated using a serial peripheral interface (SPI), which can be provided by an Arduino when running a specific software library (<SPI.h>). For the MCP413-103 chip, the SPI interface operates using three pins:

1.Chip select ( CS¯) – this pin tells the MCP413-103 to begin receiving data on the SDI pin

2.Serial clock (SCK) – this pin provides the synchronization clock for the data received (see below)

3.Serial data input/serial data output (SDI/SDO) – either separate data pins or combined (as with MCP413-103)

The overbar on the chip select pin ( CS¯) indicates that it is an active low logic chip, which means that the pin must be set to digital LOW (0V) to enable the chip. Though a full discussion of these aspects of digital electronics is outwith the scope of this book, reasons for using active low logic include noise reduction and increased fanout (the number of devices that can be connected together) due to the low activation level. In this project, each chip select pin ( CS¯) must be set to digital HIGH (+5V) when not in use, and only set LOW (0V) when serial data is being sent to that specific chip on the SDI/SDO pin.

The serial clock is needed to ensure that digital data sent by the Arduino is read correctly by the MCP413-103, effectively acting like a metronome to inform the chip when to read the incoming serial data (Figure 9.21).

Figure 9.21Digital clock synchronization example. In the example, the serial clock pin provides a synchronization pulse that tells all connected devices when to begin receiving data. If the Arduino provides the clock as the controller, any serial data it sends will be synchronized to this pulse. As a receiver, the MCP413-103 uses its SCK pin to synchronize with the clock and read the Arduino serial data correctly.

The diagram shows a serial clock example, where the controller (Arduino) provides both the synchronization clock pulse (SCK) and also the serial data that will be used to configure the digital potentiometer chips. The MCP413-103 acts as a receiver for this data on its SDI pin, where the Arduino clock signal is also connected to its SCK pin to allow it to synchronize to the incoming serial data. This simple form of clocking is used widely in digital electronics, where connected devices share a common clock pulse to ensure that they all send and receive data in synchronicity. The MIDI output examples in chapter 5 used an older form of clocking known as a universal asynchronous receiver transmitter (UART), where timing is managed by a dedicated circuit connected to both the MIDI In and OUT. Although still very robust, MIDI synchronization is now largely performed by other methods (e.g. USB) that carry the MIDI data without the need for the 5-pin DIN connector defined in the original MIDI specification.

The specific pins needed to control the MCP413-103 digital potentiometer chip are now known, so the next task is to program the Arduino to control a serial peripheral interface (SPI). To do this, the SPI library (<SPI.h>) is used to configure the Arduino for serial communications based around a standard SPI 4-pin configuration for the Arduino Uno:

Digital output pin 10: SS (slave select) – this is analogous to the chip select ( CS¯) pin on the MCP413-103, where a low input signal designates the device as a receiver. When using the Arduino as a controller, this pin should either not be connected or configured for output to avoid the Arduino being switched to receiver mode.

Digital output pin 11: MOSI (master out, slave in) – this is the SPI data output pin that is connected to the SDI/SDO pin on the MCP413-103 chip.

Digital output pin 12: MISO (master in, slave out) – this is the SPI data input pin, where the controller can receive data from connected devices (e.g. sensor chips). This pin is not needed in the final project.

Digital output pin 13: SCK (serial clock) – this pin provides the serial clock output pulse that is used to synchronize all receiving devices for data communication.

In the Arduino code, working with SPI requires a relatively small amount of setup:

The code listing above shows how Arduino digital output pins 9 and 10 are specifically configured to act as chip select ( CS¯) for more than one MCP413-103 chip (see below). The variable potInputValue was used in the previous control example to hold the data read from the analogue control potentiometer (on analogue input pin A0). The constant variable potAddress is used to define the write command the MCP413-103 chip expects when receiving a new potentiometer value, where 0x00 is an abbreviation of the full binary value 00000000 that corresponds to a potentiometer write command. The MCP413-103 chip can also respond to other commands like read potentiometer value and increment/decrement potentiometer value, but these are not needed in this project.

The setup() function configures pins 9 and 10 for output (as chip select), and then writes a digital HIGH (+5V) signal to both pins to ensure that the connected devices are not initially set to receive data – to prevent any unintentional changes to the potentiometer chips. The next line contains a function call to SPI.begin() which tells the Arduino to start the serial clock output on pin 13 – allowing all connected receivers to synchronize to this signal. By using a common clock, multiple chips can be connected to a single SPI bus that shares both the clock (SCK) and data (SDI) connections – only the chip select pin ( CS¯) must be unique for each connected chip. This is a very powerful element of protocols like SPI, where a single controller (Arduino) can control multiple receivers using a minimum amount of connections (Figure 9.22).

Figure 9.22Arduino shared SPI bus example. The diagram shows the shared clock (SCK pin 13) and serial data (SDI/SDO pin 11) SPI bus connections that are used by the final project circuit, which allow multiple MCP413-103 chips to be connected to a single Arduino as the controller. When the Arduino needs to send to a specific chip, it can set its chip select ( CS¯) pin LOW whilst leaving the other chip select HIGH. Thus, to access the low-pass filter (LPF) the Arduino sets pin 9 LOW, whilst for the high-pass filter (HPF) it sets pin 10 LOW.

The Arduino can control multiple chips from a single SDI/SDO data connection – alongside the shared serial clock running on the SCK pin. Pin 10 can be used for chip select ( CS¯ ) even though the Arduino UNO SPI configuration uses digital pin 10 as a receiver connection – the setup() command allocating it for digital output overrides this pin allocation. Each chip can now receive the same information, which allows the Arduino to run a single SPI instance, and also reduces the amount of connections needed – this is important when working with limited space on a single breadboard. To change the value on a specific digital potentiometer, three coding processes must be executed:

1.Take the CS¯ pin for the relevant chip LOW to enable receiving data

2.Send the write command over the SPI bus to the chip (only a CS¯ = LOW chip will receive the command)

3.Send the potentiometer data value over the SPI bus to the chip

These steps can be executed using a combination of digitalWrite() (to take the relevant CS¯ pin LOW) and the transfer function (SPI.transfer()) that is included in the SPI library:

The data transfer command in the code listing above sends a potentiometer value that has been reduced by a factor of 10 to the chip (i.e. the control input potentiometer value is divided by 10). This is based on the 10-bit resolution of the Arduino analogue-to-digital converter (ADC) that is used to sample input on the Analog Input pins. A 10-bit data resolution gives a range of 1024 values (0–1023), which must be reduced to map onto the 8-bit data range of the MCP413-103 potentiometer (0–127). This allows the updateDigitalPot() function from the previous stage to be updated to output data on the Arduino SPI bus:

Now the configuration and control of the MCP413-103 chips has been planned, a simple LED output circuit can be connected to each chip to test Arduino control of the digital potentiometer resistance value. To do this, the Arduino power and ground connections can be used as the input (P0A) and ground (P0B) for the MCP413-103 chip. In addition, the chips must also be powered (and grounded) in order to operate, which leads to a much more complex schematic (Figure 9.23).

Figure 9.23Arduino MCP413-103 output example. The diagram shows the full connections (pins 1–8) required to operate each MCP413-103 chip. The power (pin 8) and ground (pin 4) connections from the Arduino can also be used to provide power to an LED output circuit (pins 5 and 7) for each chip, which can be used to check that the resistance varies when updated by the SPI bus. The P0W wiper output (pin 6) is connected to a 150Ω current-limiting resistor and LED, which are also grounded by the Arduino.

The schematic shows all the connections needed to control two digital potentiometer chips. The first thing to note is the number of bridged wires on the schematic, which can initially make it appear more complex than when arranged on breadboard (where connecting wires will be physically separated from each other). Comparing the left-hand side of the chips (pins 1–4) between Figure 9.22 and Figure 9.23 shows how the ground wire has been added to pin 4 (VSS) of each chip. On the right-hand side, the Arduino power (+5V) supply is connected to pin 8 (VDD) of each chip and also to pin 5 (P0A) as the input to a voltage divider. The Arduino ground (GND) is connected to pin 7 to provide the bottom connection for the voltage divider (P0B), with pin 6 (P0W) being the wiper position that is output to the resistor/LED circuit for each chip. The resistor/LED circuits can be used to indicate the output from the MCP413-103 chips, but because these chips cannot be simulated using Tinkercad (as no simulated component for this chip is provided) a breadboard prototype must be built. This prototype can be combined with the existing input control circuit that uses a push-button and potentiometer to set the state and data variables for the system, where the state is then output to indicator LEDs (Figure 9.24).

Figure 9.24Final project stage 2 – digital potentiometer control system schematic. The diagram shows the full schematic for the second project stage, which combines the input control system from stage 1 on the left-hand side of the diagram with the digital potentiometer SPI output system on the right.

This schematic is a significant extension of the previous stage 1 circuit, where the input control system is now combined with the SPI potentiometer output LEDs. These LEDs will be replaced by audio filter circuits in the final project stage, but for now the focus will be on building a breadboard layout for the full Arduino control system (Figure 9.25).

Figure 9.25Final project stage 2 – digital potentiometer control system breadboard layout. In the diagram, the full layout and connections for the Arduino control system are shown in a Tinkercad mock-up. The actual layout for this stage will differ slightly for practical reasons, such as the wires on [b8 and b21], and the final project will replace the LEDs in the lower half of the board with the audio render system.

This Tinkercad circuit is used for reference layout purposes only, as the simulator does not list an MCP413 chip that could be used to analyse potentiometer output. The main benefit of prototyping the layout is to show the control connections between the MCP413 chips and the Arduino, as these introduce a significant amount of wiring to the breadboard circuit. Open the Arduino IDE, and create a new circuit named finalProj_FullControlSystem.ino: copy the following code and paste it into the editor:

Stage 2 – Project Steps

For stage 2 of the project, you will need:

1. Existing stage 1A circuit

2. 2 × MCP413-103 10kΩ digital potentiometers

3. 2 × LEDs (D3, D4)

4. 2 × 150Ω resistors (R3, R4)

5. 6 × connector cables and 10 × connector wires

1. Add an MCP413-103 chip across the column break with top-left pin 1 (CS) in breadboard pin f14 [MCP1–f14] – this will be the LPF chip.

2. Add an MCP413-103 chip across the column break with top-left pin 1 (CS) in breadboard pin f19 [MCP1–f19] – this will be the HPF chip.

1. Connect a connector wire between the Vss pin on the LPF chip (top-left f14) to the top ground rail [j11–JGND].

2. Connect a connector wire between the Vss pin on the HPF chip (top-left f19) to the top ground rail [j16–JGND].

1. Connect a connector wire between the SDI/SDO pins on both chips on pins [g12–g17].

2. Connect a connector wire between the SCK pins on both chips on pins [h13–h18].

1. Connect a connector wire between the Vdd pin on the LPF chip and the gap (column 15) between the chips to allow the power rail connections [d14–d15].

2. Connect a connector wire between the Vdd pin on the HPF chip and the gap (column 20) between the chips to allow the power rail connections [d19–d20].

1. Connect a connector wire between the LPF chip Vdd pin and the top power rail [e15–JV+].

2. Connect a connector wire between the HPF chip Vdd pin and the top power rail [e20–JV+].

1. Add a connecting wire between the LPF CS pin (j14) and Arduino digital output pin 9 [j14–AD9].

2. Add a connecting wire between the HPF CS pin (j19) and Arduino digital output pin 10 [j14–AD10].

Note: power-decoupling capacitors are shown in several of these images, but are not needed until stage 3.

1. Add a connecting wire between the shared SCK pins (j18) and Arduino SCK output digital pin 13 [j18–AD13].

2. Add a connecting wire between the shared SDI/SDO pins (j17) and Arduino MOSI output pin 11 [j17–AD11].

1. Use a small cable tie to group the SPI communication cables together – the image shows a spare loop that appears conjoined but is actually separate from the switch/state LED wires.

1. Connect a connector wire between the LPF chip P0B pin (pin 7) and the bottom ground rail [a13–AGND] – the reference image is slightly blurred.

2. Connect a connector wire between the HPF chip P0B pin (pin 7) and the bottom ground rail [a18–AGND].

1. Connect a 150Ω current-limiting resistors across rows [b8–b12] – the reference image is slightly blurred.

2. Connect a second 150Ω current limiting resistor across rows [b17–b21].

3. Connect a potentiometer test LED to the bottom ground rail across rows [b8–AGND].

4. Connect a potentiometer test LED to the bottom ground rail across rows [b21–AGND].

1. Add a connecting wire between the LPF P0A pin and the bottom power rail [d11–J+] – the reference image is slightly blurred.

2. Add a connecting wire between the HPF P0A pin and the bottom power rail [d16–J+].

The top power rail is used for all power lines in this project to avoid having a bottom power rail in the final circuit – this will help reduce noise in the audio system, which uses the bottom ground rail between audio connectors.

1. Add a bridging connector across the breadboard ground rails [JGND–AGND].

2. Connect the Arduino +5V pin to the top positive rail [AD+–J+].

Compile the code to check for copy errors and then upload the sketch to the Arduino Uno to test the circuit. If everything compiles correctly, each time the LPF (LED 1) or HPF (LED 2) states are selected with the push-button the analogue potentiometer value should change the brightness of the test output LEDs connected to the MCP413-103 digital potentiometers.

The circuit shown above represents the completed Arduino control system for the final project, where a single analogue potentiometer (as a control input to the Arduino) is used to set the value of the two MCP413-103 digital potentiometers. When the system state is set to no change then the control potentiometer has no effect, but when a specific filter (LPF/HPF) is selected then the specific test output LEDs should increase and reduce in brightness based on the control potentiometer value. Note that when another filter is selected the current digital potentiometer value is retained – when the chip select pin ( CS¯) is taken HIGH the MCP413-103 will no longer receive write messages or data.

This circuit can now be extended to include the 2-band equalizer amplifier circuit that was first built in chapter 8. The layout of this circuit is broadly similar to that chapter, but some small changes are required to align the filter components with the digital potentiometer input pins (P0A, P0W, P0B). The next section will build the last stage of the final project in this book, so if you are completing this stage late at night (or do not have significant time to spare) then it is recommended to take a short break at this point – to avoid potential errors that may be introduced to an already functional circuit.

9.4 Final project – Arduino-controlled audio amplifier with 2-band equalizer

The final stage of this project combines the Arduino digital control system built in stages 1 and 2 with a 2-band equalizer amplifier derived from the circuit built in chapter 8. To avoid confusion when working with this more complex system, the Audio render system can be broken down into sections – the first of which shows how the MCP413-103 digital potentiometers are used to control the audio filter inputs in the circuit (Figure 9.26).

Figure 9.26Digital potentiometer audio filter control. The right-hand side of the schematic shows how the audio input connection (Audio_IN) is split between two filter stages – the bottom low-pass filter (RC network) and the top high-pass filter (CR network) which are both connected to the common Arduino GND. The left-hand of the schematic shows the two MCP413-103 digital potentiometer chips that take these filter signals as inputs to a voltage divider (between P0A and P0B) where the combined pin 6 (P0W) outputs provide the Audio_OUT signal that will be connected to the LM386 amplifier.

In the schematic, the digital potentiometers from the circuit shown in Figure 9.22 and Figure 9.23 are now connected to low- and high-pass audio filters. The audio input (Audio_IN) connection is split between both filters, where the output of each filter is connected to pin 5 (P0A) of the MCP413-103 chips. It may initially be difficult to follow this circuit path, as the previous chapter on audio filters used the conventional linear arrangement of components that is most commonly seen in audio schematics. The rearranged layout shown in the diagram in Figure 9.26, where both filters are now connected from right to left, still represents exactly the same circuit paths as those shown in chapter 8. The additional change for the low-pass filter, where the filter is effectively flipped over so that the ground connection is at the top, also makes no difference to the actual current flow through the filter. In each case, it can be useful to think of the signal path of each filter in the following order:

In the schematic on Figure 9.26, the input signal is simply split between both filter stages (this is often seen in guitar tone controls and older instrument amplifiers). In practice, splitting the input signal would require some form of buffer amplifier circuit to prevent significant attenuation of the input signal, but a passive splitter is more than sufficient for this project circuit. Each filter is also connected to a common ground provided by the Arduino (Arduino_GND), which is also used to ground the MCP413-103 chips on pin 7 (P0B). The ground connection acts as the third pin of the voltage divider provided by the digital potentiometer, where each filter signal will be split depending on the resistance value presented to it by its associated MCP413-103 chip.

The output of each digital filter is taken from the wiper terminal on pin 6 (P0W) and summed to provide the audio output signal (Audio_OUT). Once again, a practical circuit would employ some form of summing amplifier circuit to prevent further attenuation, but as the project focusses more on the digital control of an analogue audio circuit these extra stages are not necessary (and would require significant extra components and breadboard space). This summed signal can then be amplified to provide an output that will drive a suitable loudspeaker actuator (Figure 9.27).

Figure 9.27Digital potentiometer 2-band equalizer amplifier circuit. The schematic shows an extension of the previous Figure 9.26 schematic, where the combined audio output from the P0W wiper pins (pin 6) of both MCP413-103 chips is fed into the non-inverting input of an LM386 audio amplifier. AC decoupling capacitors (C5, C6) are connected across the power rails to reduce noise, whilst a DC blocking capacitor (C4) is connected at the LM386 output to protect the chip. A Zobel network (C3, R5) is also connected in parallel to the amplifier output to balance the inductive impedance of any loudspeaker connected to Audio_OUT. Notice the ground connection between the Audio_IN and Audio_OUT connectors that is then connected to the Arduino as the common ground for the circuit – this shorter ground path helps to reduce circuit noise.

The right-hand side of the circuit shown in Figure 9.27 represents the complete audio render system for the final project, which combines two audio filter networks with an LM386 audio amplifier. This circuit takes the P0W wiper outputs (pin 6) from each digital potentiometer and connects them to the non-inverting input of the LM386 audio amplifier – the inverting input on pin 2 is connected to the common system ground from the Arduino. The output of the amplifier is connected in series with a DC blocking capacitor (C4) to the Audio_OUT connector, which protects the amplifier from any current surges coming back into the circuit. In addition, a Zobel network (C3, R5) is connected in parallel with the audio output to help balance the inductive impedance load of any loudspeaker that may be connected to the circuit (where the inductive reactance will increase with frequency).

AC decoupling capacitors (C5, C6) are also connected across the power rails to reduce noise in the circuit, which is particularly important when combining analogue audio and digital control signals (from the Arduino) on a breadboard with common power rails. Digital signals operate at a far higher level (+5V) than a typical analogue audio input such as a consumer line level at around 0.3VRMS (see chapter 7, Table 7.1). This discrepancy in signal level is compounded when working with communications protocols like the SPI bus, where rapid fluctuations in voltage levels during to the transmission of binary data (+5/0V) can create significant noise throughout the circuit. It should be noted that whilst the Arduino is a very flexible microcontroller, it is primarily a digital device that either samples analogue input (to convert it to digital data) or employs techniques like pulse wave modulation (PWM) to mimic analogue voltage output levels (to convert digital to analogue) – it is not an analogue device. In addition, digital noise reduction has a very different set of requirements and thresholds as it will never be conveyed for human listening, and so microcontrollers like the Arduino are not optimized to work directly with analogue audio signals.

This shared access to power rails is also the reason for the additional ground connection between the Audio_IN and Audio_OUT connectors shown in the schematic above. Although all ground terminals in the circuit are connected to the Arduino, a short ground path between the audio connectors in the circuit will help to reduce noise introduced by other elements – as the audio signals have a ground path that does not pass directly through the digital control circuit. It is good practice to keep audio ground paths as short as possible when working with combined systems and, as a further element of noise reduction, the bottom +5V power rail [J+] is not connected in the final breadboard layout to help isolate all audio render system ground connections from the Arduino supply (this requires the LM386 to be oriented in the other direction on the breadboard). Although these types of design consideration are arguably more advanced than required for an introductory text that works with breadboard prototypes, and are also not in themselves a robust or comprehensive noise-reduction strategy, they are included to help illustrate the need for power-supply isolation in more advanced schematics that you may encounter as your learning progresses beyond this book.

With the audio amplifier circuit added to the digitally controlled filter stages, the final project circuit for an Arduino controlled 2-band equalizer amplifier can be produced (Figure 9.28).

Figure 9.28Final Arduino-controlled 2-band equalizer amplifier circuit. The schematic shows how the previous Arduino control system (developed in stages 1 and 2 of the project) can now be connected to two audio filter stages, where the filter cutoffs are set at 100Hz (low pass) and 1kHz (high pass). These filters are connected to the MCP413-103 digital potentiometers, whose wiper output on P0W (pin 6) is then combined and connected to the non-inverting input of an LM386 audio amplifier. The minimal design of the LM386 is derived from its datasheet to include a Zobel network and DC output blocking (C4), and additional AC decoupling capacitors (C5, C6) are also added to reduce noise on the power rails for the system.

This schematic combines the Arduino digital control system (left-hand side) and the audio render system (right-hand side) within a single circuit. The control inputs on the left (push-button switch and analogue potentiometer) are used to set the state and data of the system, where each filter output is defined as a separate state (with no filter output being the default). The Arduino controls two MCP413-103 digital potentiometers using an SPI bus (Arduino pins 9, 10 11, 13) that can set the value of each potentiometer using the CS¯ control connections on Arduino pins 9 and 10 (MCP413 pin 1) to select each chip. On the right-hand side of the schematic, the audio input (Audio_IN) is split between a low- and a high-pass filter that are then connected to the potentiometer P0A input pins (pin 5) of the MCP413-103 chips. The output P0W wiper pins (pin 6) are then recombined at the non-inverting input to an LM386 audio amplifier. This amplifier also has AC decoupling (C5, C6), a Zobel network (C3, R5) and DC blocking (C4) to stabilize and protect the audio output (Audio_OUT) from the circuit, which has an additional ground connection to the audio input for noise reduction.

The Arduino provides both power and ground connections, and, as noted at the beginning of this chapter, the bottom power rails will be swapped over in this final build stage. This can initially be confusing, but it is important to remember that breadboard rails are simply marked for reference – the rail itself does not change the connections to it! Swapping the power rails at the bottom of the breadboard allows resistor components to be directly connected between columns and ground without the need for additional bridging wires, which saves much-needed space in the final circuit. For this reason it is important to note that:

Using the bottom rail for the audio render system ground connections also allows the audio ground rail to be kept further apart from the Arduino control rails at the top of the breadboard, helping to minimize noise by giving both audio connectors a ground path that does not directly include the Arduino power supply. Whilst this is not a comprehensive noise-reduction strategy (given that this is a breadboard prototype circuit), it is again noted that keeping audio signal paths (and ground connections) as isolated as possible can help to reduce the amount of noise introduced into the signal by control system processes like the SPI bus.

Although this circuit cannot be fully simulated using Tinkercad (which is primarily designed for control system simulation), the layout for the project can be presented as a mock-up for reference purposes when building the final circuit (Figure 9.29).

Figure 9.29Final project stage 3 – digital potentiometer control system breadboard layout. In the diagram, the top half of the breadboard shows the existing Arduino control system from stages 1 and 2 where additional AC decoupling capacitors on the top power rails are spaced further apart and the analogue potentiometer has been rotated for visual clarity. The bottom half of the board is the audio render system, where the audio input [c1–c3] is connected to two filter stages whose outputs are fed into the P0A input (pin 5) of the MCP413-103 digital potentiometers. The P0W outputs of these potentiometers (pin 6) are connected to the non-inverting input of an LM386 audio amplifier (note pin 1 of the amplifier is oriented in the opposite direction to the potentiometer chips). The amplifier output circuit is broadly similar to those of chapters 7 and 8, where a Zobel network and DC blocking capacitor [b26–b28] are used to balance the connection to an output loudspeaker.

The breadboard layout shown in Figure 9.29 extends the previous Arduino digital control system layout of stage 2 (where the LED test output circuits have now been removed). There are several elements to note before beginning to build the final stage of the project:

1.The audio input connector on pins [c1–c3] is connected to the bottom ground rail (which is labelled V+) that is in turn connected to the lower ground connectors on the Arduino – this also keeps the layout less cluttered by not adding a bridging wire across the rails (as in previous projects).

2.The audio input is split at pin c3 to go through a low-pass RC filter 160kΩ resistor [a3–a7] and 10nF capacitor [c7–c8], and a high-pass CR filter 10nF capacitor [c9–c10] and 16kΩ resistor [a10–AGND].

3.The low-pass filter output is connected to the P0A (pin 5) of the first MCP413-103 chip on pins [d7–d11] – this is not clearly visible in the layout above.

4.The high-pass filter output is connected to the P0A (pin 5) of the second MCP413-103 chip on pins [b10–b16].

5.Both MCP413-103 P0W wiper outputs (pin 6) are connected to the non-inverting input (pin 3) of an audio amplifier [c12–c23] and [b17–b23] – note that the LM386 is oriented in the opposite direction to the other chips.

6.The LM386 output (pin 5) is connected to a Zobel network of a 0.05μF capacitor [f24–f25] and 10Ω resistor [j25–JGND], and also to a 250μF DC blocking capacitor [b26–b28] that is connected in series to the audio output connector [c28–c30].

7.A bridging wire is added to ground the LM386 inverting input [d22–d24] and bridging wire is also used to create the parallel path for the Zobel network [h24–h26] and [e26–f26].

8.The AC decoupling capacitors (1μF and 0.1μF) are connected further down the top power rails for visual clarity, but will be placed closer to the Arduino power connections on the top left of the board in the actual circuit build.

9.The LM386 gain (pins 1 and 8) and bypass (pin 7) pins are left unconnected to save components and space, but in practice pin 7 would be grounded and some gain control would be applied to keep the amplifier stable.

Stage 3 – project steps

For Stage 3 of the project, you will need:

1. Existing stage 2 circuit (minus test LEDs) – the image shows two ground connectors already running to the bottom AGND

2. Two filter resistors – low-pass 160kΩ resistor (R3), high-pass resistor 16kΩ (R4)

3. Two filter capacitors C1 = C2 = 10nF

4. 1 × LM386 operational amplifier

5. Zobel network – 10Ω resistor (R5), 0.05μF capacitor (C3)

6. DC blocking capacitor (LM386 output) – 250μF capacitor (C4)

7. Power-decoupling capacitors – 1μF capacitor (C5) and 0.1μF capacitor (C6)

8. 1 × audio input connector (3.5mm jack), 1 × audio output connector (loudspeaker)

9. 2 × connector cables and 12 × connector wires

1. Add an audio input connector across pins [c1–c3].

2. Ground the input connector terminal by connecting its ground (pin 1) to the bottom circuit ground which is marked as the V+ rail between pins [b1–AGND].

1. Connect the 160kΩ low-pass filter resistor (R3) across pins [a3–a7].

2. Connect the 10nF low-pass filter capacitor (C1) across pins [c7–c8].

3. Connect the network to ground [a8–AGND].

1. Connect a connector wire from the audio input to the high-pass filter network [b3–b9] – pin partially obscured by capacitor.

2. Connect the 10nF high-pass filter capacitor (C2) across pins [c9–c10].

3. Connect the 16kΩ high-pass filter resistor (R4) to ground across pins [a10–AGND].

1. Connect a connector wire from the low-pass filter output to the P0A input (pin 5) of the 1st MCP413-103 chip [b3-b9] – wire partially obscured by capacitors.

2. Connect a connector wire from the high-pass filter output to the P0A input (pin 5) of the 2nd MCP413-103 chip [b10–b16].

3. Connect pin 3 of the LPF MCP413-103 chip to ground with a connector wire between pins [a13–AGND].

1. Connect pin 3 of the HPF MCP413-103 chip to ground with a connector wire between pins [a18–AGND].

2. Add the LM386 audio amplifier with pin 1 on breadboard pin e21 – note this is in the opposite orientation to the digital potentiometer chips.

3. Connect a connector wire from the inverting input (pin 2) to the LM386 ground pin (pin 4) [d22–d24].

4. Connect the LM386 ground pin (pin 4) to the breadboard ground across pins [a24–AGND].

1. Connect the LM386 Vs (pin 6) to the top power rail [j23-J+].

The LM386 Vs is oriented with pin 6 facing the top of the breadboard to keep all power connections on the top half of the board. This helps to reduce the overall noise in the circuit, as no power connection is made to the bottom of the board where the audio connectors are grounded.

1. Connect a connector wire from the LPF MCP413-103 chip P0W wiper output to the non-inverting input (pin 3) of the LM386 between pins [c12–c23].

2. Connect a connector wire from the HPF MCP413-103 chip P0W wiper output to the non-inverting input (pin 3) of the LM386 between pins [b17–b23].

1. Ground the output connector terminal by connecting its ground pin to the bottom ground rail between pins [b30–AGND].

1. Begin building the Zobel network by adding a connector wire between pins [h24–h26] to link to the LM386 output (pin 5).

2. Add a second connector wire between pins [e26–f26] to bridge the centre column of the breadboard and link to the LM386 output.

1. Add the Zobel network 0.05μF capacitor [f24–f25]

2. Complete the Zobel network with a 10Ω resistor [j25–JGND]

1. Add a 250μF DC blocking capacitor to connect the LM386 output to the audio output connector on pins [b26–b28].

1. Add a 1μF AC decoupling capacitor across the top power rails, as close as possible to the Arduino connector wires – the image shows the fourth column of the power rails

2. Add a 0.1μF AC decoupling capacitor across the top power rails, as close as possible to the Arduino connector wires – the image shows the seventh column of the power rails.

1. Add a ground connector cable from the bottom Arduino ground pin [ADGND] to the bottom breadboard ground rail, which is V+ in this circuit [ADGND–AGND].

2. Add a power connector cable from the Arduino +5V pin to the top breadboard power rail [AD+–AV+].

Note: the bottom power rail (V+) will be used as a ground rail (AGND) in the final build stage.

Connect the 3.5mm audio jack to an audio playback device. If everything has been connected properly, the audio input signal should be fed through the low- and high-pass filters, and their combined output should be amplified by the LM386 for output to a connected loudspeaker. The previously uploaded code from stage 2 should still be running on the Arduino, so each time the LPF (LED 1) or HPF (LED 2) states are selected with the push-button the analogue potentiometer value should change the filter level of either the low-pass filter (LPF) or the high-pass filter (HPF) connected to the MCP413-103 digital potentiometers. This should be audible through the connected loudspeaker, though given the prototype nature of the project high-quality filter performance should not be expected.

9.5 Conclusions

This chapter has combined concepts from previous chapters to build a more expansive electronics circuit that combines both analogue audio rendering and Arduino digital control:

1.Chapter 5 – making decisions based on control inputs (i.e. switches and piezoelectric sensors)

2.Chapter 7 – amplifying an audio signal (using the LM386 to drive a loudspeaker as an output actuator)

3.Chapter 8 – filtering an audio signal for amplification (the 2-band equalizer amplifier circuit)

In the final project, the Arduino control system uses a push-button switch to control the state of the system, whilst an analogue input potentiometer controls the data used to vary the digital potentiometers. These digital potentiometers are connected to the Arduino using an SPI bus, which acts as the interface between the Arduino control system and the audio render system. The audio render system combines low-pass (LPF) and high-pass (HPF) filter stages that are controlled by the digital potentiometers and connects their output to an LM386 audio amplifier to drive a loudspeaker. In so doing, an Arduino-controlled audio amplifier with 2-band equalizer has been built.

This project represents a significant increase in time and complexity from previous chapter projects, where the combination of the Arduino control system with an audio render system introduced a lot of additional practical constraints. The breadboard layout for this project is much more involved, and many workaround techniques (such as swapping the power and ground rails at the bottom of the board) were employed to fit everything together within a small working area. This was done for two reasons: to keep costs down, and to demonstrate some of the practical constraints of circuit design and layout (where space is a cost in itself). More advanced texts will discuss the importance of short signal paths and the related problem of component heat management (due to proximity), but for this book the summative point is the combination of analogue audio rendering with digital Arduino control.

The final chapter provides a short summary of all that you have learned during this book, to link these elements together and also make some suggestions for how you may now progress your learning beyond the scope of this introductory text. It is important to read this chapter to remind yourself of the distance you have travelled in completing this book. It is hoped that the practical nature of the circuits involved will help to inspire future interest and investigation within this subject area.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.32.86