KEY FEATURES:

  • Rotary Volume Control
  • Touch Based Mute/Unmute Control
  • Automatic Potentiometer Position Determination and Calculation (this means that anytime the device is unplugged and plugged back into a computer the code calculates the position of the potentiometer and sets the correct corresponding volume automatically. If the device were to be plugged in and the pot set to the lowest setting the computers volume would be at its minimum. If the device were to then be unplugged and have its pot knob set to the max and then plugged back in, the onboard code would recalculate the pot position and the computers volume would be set to the max.)
  • LED's which visually indicate whether volume is enabled. (Green : enabled, Red : disabled)

RP2040 Pins and Connections

The 3.3V pin is connected to a common positive rail and the GND rail is connected to a common negative rail. This voltage is used to power both the Red/Green LED's as well as supply voltage to the potentiometer which will essentially be used as a variable voltage divider. Pin A0 is being used as the main ADC pin and is connected to the wiper terminal of the potentiometer, Pin A3 is being designated as the capacitive touch pin for touch based input, pin D8 is driving the Red LED, and pin D9 is driving the Green LED.

Schematic

This is the wiring diagram for the controller. While the board being used is not a Pico, the 2040 chip is the same on both boards with most of the pins overlapping. The red wire connected to GPIO pin 3 (GP3) is the capacitive touch end.

Volume Adjustment Principle of Operation and Logic Dissection

Analog and ADC Workings: 

The volume control works by leveraging the potentiometer which has one terminal connected to 3.3V and the other to ground. The potentiometer has a wiper which sweeps over a resistive material which is connected from 3.3V to ground, as the wiper is moved (rotated via the knob) from the 3.3V terminal to the ground terminal, the voltage read out (output) from the wiper terminal increases to the maximum available voltage as the wiper is rotated to the ground terminal position. Voltage increases due to V=IR. If current is fixed, then an increase in resistance will lead to an increase in voltage. This effect occurs in the potentiometer since as you are rotating the wiper closer to the ground terminal, the effective length and thus resistance of the resistive material increases. 

Using the onboard ADC (Analog to Digital Converter) we can convert the continuous analog voltage signal from the potentiometer into a discrete digital signal which works by sampling the signal and providing integer values in place of continuous (floating point) values. Whenever I used the ADC to read the min and max voltage output from the potentiometer [0 to 3.3V], I found that the ADC provided integer values in the region of around 0 to 64700. Since analog values are subject to change and fluctuation, the upper reading was an average of multiple readings. Since working with such large numbers is a bit unruly,  I implemented a function which converts the integer values into a corresponding 0 to 3.3 range, essentially converting our data stream into a voltage representation now that the data has been taken in by the microcontroller.

def get_voltage(pin):
    # converting ADC pin values to a 0 to ~3.3V range (accuracy may vary)
    # avg ADC max is around 64700
    return (pin * 3.3) / 64700

Code and Logic: 

There are 16 volume divisions for a Mac's volume parameter (17 if you include mute) as shown here in the volume icon which appears when adjusting the volume.

Since there are 16 divisions, create an array of volume boundaries which is essentially just the digitized and voltage converted max output range of the potentiometer (~3.3) divided by 16 possible volume divisions. This is accomplished by first creating an equivalent volume 'unit' for our code which is done by creating the fraction: (Max output) / (Max divisions)...

Read more »