Skip to main content

Potentiometer

You have learned a lot about digital signals and tried many projects with digital devices. Now let's know something about analog signals. The potentiometer is a typical component to give you analog readings. You'll connect a potentiometer to your circuit and read analog values.

Learning goals

  • Learn about analog signals and distinguish them from digital signals.
  • Learn how ADC works.
  • Understand the working principle of potentiometers.
  • Learn how the voltage divider works in circuits.
  • Use the serial monitor to view printed values.

🔸Background

What is an analog signal?

As mentioned before, the signals can be divided into two types: digital signal and analog signal. Unlike the digital signal changing among several values, the analog signal is continuous. Its voltages change smoothly with time, which means there are infinite possible values from minimum to maximum voltage (0 - 3.3V).

Analog signal

Peripheral - ADC

But the microcontrollers are digital components, how do they read the analog voltage at a specific time?

Indeed, the microcontrollers cannot directly read analog values. It needs an analog-to-digital converter (ADC). The ADC works by sampling the analog signal at discrete intervals and then converting the measured voltage to a corresponding digital value that can be processed by the microcontroller. The digital value represents the amplitude of the analog signal at the time it was sampled.

The ability to measure analog values depends on the ADC resolution. The resolution tells the number of possible values. For example, if the resolution is 2-bit, there can be 22 = 4 possible values: 0, 1, 2, 3. These values are called raw values. And if it's 3-bit, there are 23 = 8 values (0-7).

ADC resolution

The digital values output by an ADC are often referred to as "raw" values, and they may not be easy to understand or use. To make sense of these values, they need to be mapped back to their corresponding voltage values.

To do this mapping, you need to know the voltage range that the ADC is measuring, 0 to 3.3V in this case. The SwiftIO Micro has a built-in 12-bit ADC, so there are 4096 (212) levels, from 0 to 4095. Therefore, if the raw value equals 0, the voltage would be 0V; if the raw value equals 4095, the voltage would be 3.3V; and 1365 corresponds to (3.3 / 4095) * 1365 = 1.1V.

Here is the equation for the conversion:

voltage = (3.3 / 4095) * raw value

According to the formula, you can infer, higher resolution means more possible values, so the voltage obtained will be more accurate. For instance, if you get a raw value of 6 with a 3-bit ADC, the voltage should be around 3.3 / 7 * 6 ≈ 2.8V. But the same voltage might be considered as 3.3V with a 2-bit ADC.

🔸New component

Potentiometer

Potentiometer (pot for short, also called knob) is a kind of variable resistor. You can rotate it to change its resistance and thus change the voltage in a circuit. It is commonly used to control the volume on audio devices, as a menu selector knob on home appliances, etc.

The potentiometer has a movable wiper inside it. As you rotate it clockwise or anticlockwise, the wiper moves with it to change the available resistance in the circuit.

potentiometer

Symbols: International potentiometer symbol(international), American potentiometer symbol(US)

As shown in the image above, it has three terminals. The resistance between two ends (a and c) represents the maximum resistance. The middle one is connected to a wiper. As you rotate the knob, the position of the wiper changes and the resistances on two side of the wiper changes proportionally.

For example, if you connect the outer pin a to the ground, the other outer pin c to power, and the middle pin b to the input pin, let's see how it works.

  • When you turn it to make the wiper move gradually far away from a, the actual resistance between a and b in the circuit increases. And the voltage measured on pin b would increase with it, gradually to 3.3V.
  • If you turn it in the opposite direction, the voltage on pin b would decrease to 0.
Rotate a potentiometer

🔸New concept

Voltage divider

A voltage divider circuit is commonly used to get a voltage that is a fraction of the input voltage. Usually, there are resistors connected in series. As the current flows through the resistors, the voltage drops.

Voltage divider

The voltage divider calculation is based on ohm's law. Here's the equation:

VR1 = Vin * R1 / (R1 + R2)

Vin: input voltage, VR1: voltage drop on R1

The two resistors are in series, which means the current flows through them are the same, so VR1 / RR1 = Vin / (RR1 + RR2). Then you can get the formula above.

The potentiometer is a typical example of it. The whole resistor is cut into two parts by the wiper. As the wiper goes, the ratio of the resistor changes, thus the voltage drop changes accordingly.

🔸Circuit - Potentiometer

There are two potentiometers on your SwiftIO Playground. They are connected respectively to A0 and A11.

Potentiometer module circuit
Potentiometer PinSwiftIO Micro Pin
SIGA0
NC (Not Connected)-
3V33V3
GNDGND
Potentiometer module circuit diagram
note

The circuits above are simplified for your reference. Download the schematics here.

🔸API

Class

AnalogIn - read the analog input.

MethodExplanation
init(_:)Initialize an analog pin. The only parameter is the pin id.
readVoltage()Read the voltage value on the pin. The return value is a float between 0 and 3.3.
readPercent()Read the input value and represent it as a percentage. The return value is between 0 and 1.

Global function

print(_:) - print out the value which you can view on any serial monitor.

🔸Projects

1. Reading input

In this project, you will rotate the potentiometer to change the input value. You will view the voltage value on the serial monitor.

Project overview

  • After you download the code and connect the port to the serial monitor, the value is printed on the window one by one per second.
  • When you turn the potentiometer, the value changes. It gradually goes to the maximum or minimum value according to the direction you twist.
Read analog values changed by potentiometer

Example code

You can download the project source code here.

main.swift
// Import SwiftIO library to control input and output, and MadBoard to use the pin name.
import SwiftIO
import MadBoard


// Initialize the analog pin A0 for the potentiometer.
let pot = AnalogIn(Id.A0)

// Read the voltage value and print it out every second.
while true {
let potVoltage = pot.readVoltage()
print(potVoltage)
sleep(ms: 1000)
}

Code analysis

let potVoltage = knob.readVoltage()

Declare a constant to store the returned voltage value.

There are three different methods in AnalogIn class to read analog input. The only difference is the forms of the return value: raw value, voltage value, or percentage. The method readVoltage() returns the voltage reading.

How to use the serial monitor?
  1. After finishing downloading the project, click SERIAL MONITOR tab.
  1. Choose the port of your board. Its name should start with tty.wchusbserial.
  1. Select the baudrate 115200.
  1. Click the button Start Monitoring.

And then you should see the values printed one after another.

note

Before you download your project to the board, make sure to disconnect the port.

2. Pitch control

You will use a potentiometer to control the sound from the buzzer.

Project overview

  • Read the analog value from the potentiometer in percentage.
  • Set the PWM frequency based on the value.
  • Therefore, the pitch becomes higher when you turn it clockwise and lower when you turn it anticlockwise.
Change buzzer sound with a potentiometer

Example code

You can download the project source code here.

main.swift
// Import the SwiftIO to control input and output and the MadBoard to use the pin name.
import SwiftIO
import MadBoard


// Initialize the analog pin for the potentiometer and PWM pin for the LED.
let pot = AnalogIn(Id.A0)
let buzzer = PWMOut(Id.PWM5A)

// Read the input value in percentage.
// Then calculate the value into the frequency.
// Set the PWM with the frequency and a duty cycle.
while true {
let potPercentage = pot.readPercent()
let frequency = 50 + Int(1000 * potPercentage)
buzzer.set(frequency: frequency, dutycycle: 0.5)
sleep(ms: 20)
}

Code analysis

let potPercentage = pot.readPercent()

Here you use another method readPercent() to get the input value represented as a percentage.

let frequency = 50 + Int(1000 * potPercentage)

The reading value is too small to serve as frequencies. You then map it to a bigger value to get a frequency from 50 to 1050.

buzzer.set(frequency: frequency, dutycycle: 0.5)

The PWM pins will output signals with the specified frequency to drive the buzzer. In this way, when you turn the potentiometer, the buzzer pitch will change gradually.

3. LED dimmer

The potentiometer can also be used to change LED brightness. In this project, you'll turn the potentiometer to brighten and dim an LED.

Project overview

  • Read the analog value from the potentiometer in percentage.
  • Set the PWM duty cycle using this value.
  • Therefore, the LED becomes brighter when you turn it clockwise and dimmer when you turn it anticlockwise.
Change LED brightness with a potentiometer

Example code

You can download the project source code here.

main.swift
// Import SwiftIO library to control input and output, and MadBoard to use the pin name.
import SwiftIO
import MadBoard


// Initialize the analog pin for the potentiometer and PWM pin for the LED.
let pot = AnalogIn(Id.A0)
let led = PWMOut(Id.PWM4A)

// Read the input value.
// The value is represented in percentage, while the duty cycle is also between 0 and 1,
// so you can directly use the reading value to set the PWM.
while true {
let dutycycle = pot.readPercent()
led.setDutycycle(dutycycle)

sleep(ms: 20)
}

Blink the LED. The LED brightness is changed by the potentiometer (A0). The blink rate is changed by the potentiometer (A11).

Project overview

  • Read analog value from the potentiometer (A0) in percentage. This value is used to set the duty cycle of the PWM signal, hence it adjusts LED brightness.
  • Read raw analog value from the potentiometer (A11). This value sets how long the PWM signal will last, and thus change the LED blink rate.

Example code

You can download the project source code here.

main.swift
// Use two potentiometers to control LED.
// One changes LED brightness and the other changes the blink rate.

// Import SwiftIO to control input and output.
import SwiftIO
// Import MadBoard to use the pin ids.
import MadBoard


// Initialize the analog pin used to adjust LED brightness.
let brightnessPot = AnalogIn(Id.A0)
// Initialize the analog pin used to change blink rate.
let blinkRatePot = AnalogIn(Id.A11)
// Initialize the PWM pin for the LED.
let led = PWMOut(Id.PWM4A)

while true {
// Read raw value from the analog pin. It sets the LED blink rate.
let blinkTime = blinkRatePot.readRawValue() / 2

// Read value from the analog pin.
// It serves as duty cycle for PWM to set LED brightness.
led.setDutycycle(brightnessPot.readPercent())
sleep(ms: blinkTime)

// After a specified time, suspend the output to turn off the LED.
led.suspend()
sleep(ms: blinkTime)
}

5. Double LED dimmer

In this project, two potentiometers work together to control the LED brightness. One controls the maximum intensity and the other adjusts the brightness between the minimum and the maximum.

Project overview

  • Read the analog value for potentiometer A11 in percentage. Map it into a specified maximum duty cycle range, which determines the maximum LED brightness.
  • Read the analog value for potentiometer A0 in percentage. It is used to adjust LED brightness between the darkest and the maximum.
  • Multiply the two values above to get a final duty cycle.
  • Set the PWM signal with the given duty cycle to adjust LED brightness.

Example code

You can download the project source code here.

main.swift
// In this project, two potentiometers control the LED brightness.
// One controls the maximum intensity and the other adjusts the brightness between the minimum and the maximum.

// Import SwiftIO to control input and output.
import SwiftIO
// Import MadBoard to use the pin ids.
import MadBoard


// Initialize the analog pin used to adjust LED brightness from min and max.
let brightnessPot = AnalogIn(Id.A0)
// Initialize the analog pin used to set max brightness.
let intensityPot = AnalogIn(Id.A11)
// Initialize the PWM pin for the LED.
let led = PWMOut(Id.PWM4A)

// Define the available range of max duty cycle adjusted by A11.
let minIntensity: Float = 0.2
let maxIntensity: Float = 1

while true {
// Map the analog value from the range 0-1 to the range minIntensity-maxIntensity.
// It decides the max duty cycle for the PWM signal.
// That's to say, it changes the maximum intensity of the LED.
let maxDutycycle = intensityPot.readPercent() * (maxIntensity - minIntensity) + minIntensity

// Read the analog value (0-1) from the pin A0 which serves as ratio for final duty cycle.
let dutycycleRatio = brightnessPot.readPercent()

// Calculate the final duty cycle value (0-maxDutycycle).
// Set the PWM output using the result.
led.setDutycycle(dutycycleRatio * maxDutycycle)
sleep(ms: 20)
}

6. Playing tones

Play a scale manually. Rotate the potentiometer to play notes from C4 to C5 one by one.

Project overview

  • Store the frequencies of musical notes in an array.
  • Read raw analog value from the potentiometer and map it into the index of the array.
  • If the new index is different from the last one, set the PWM signal using the corresponding frequency to play the note.

Example code

You can download the project source code here.

main.swift
// Play scales by rotating a potentiometer slowly.

// Import the SwiftIO library to set the input and output, and the MadBoard to use the pin ids.
import SwiftIO
import MadBoard


// Initialize a PWM pin for the buzzer.
let buzzer = PWMOut(Id.PWM5A)
// Initialize a analog pin for the potentiometer.
let pot = AnalogIn(Id.A0)

// Declare a constant to store an array of frequencies.
// They matches respectively notes C4, D4, E4, F4, G4, A4, B4, C5.
let frequencies = [262, 294, 330, 349, 392, 440, 494, 523]

// Store the last frequency index to avoid playing the same note.
var lastFrequencyIndex = frequencies.count

while true {
// Read the raw analog value from the pin.
// Map the value into the index of the array.
let frequencyIndex = Int((Float(pot.readRawValue() * (frequencies.count - 1)) / Float(pot.maxRawValue)).rounded(.toNearestOrAwayFromZero))

// Check if the note hasn't been played.
if frequencyIndex != lastFrequencyIndex {
// Play the new note for 500ms.
buzzer.set(frequency: frequencies[frequencyIndex], dutycycle: 0.5)
sleep(ms: 500)
buzzer.suspend()
lastFrequencyIndex = frequencyIndex
}
sleep(ms: 10)
}