In a previous lab we covered how to use an Analog Input to control the brightness of a PWM Output, where a higher number from the Analog Input results in higher duty cycle / a brighter LED. What if we want the opposite effect: a higher number from the Analog Input results in a dimmer LED? In order to do so we will need to use linear interpolation.
Linear Interpolation is the estimation of an unknown value that falls within two known values. If we have some input value inputValue, and we know that inputValue will be between inputMin and inputMax, we can calculate the relative position of inputValue between the new values outputMin and outputMax. In other words
outputValue = (inputValue - inputMin) * (outputMax - outputMin) / (inputMax - inputMin) + outputMin
Lets define (def) a function (called convert) to test
def convert(inputValue, inputMin, inputMax, outputMin, outputMax):
outputVal = (inputVal - inputMin) * (outputMax - outputMin) / (inputMax - inputMin) + outputMin
return outputVal
Now let's try converting a known number from one range to another
for i in range(10):
print(i, convert(i, 0, 10, 0, 500))
One small adjustment: running the above code results in a floating point output. If we simply adjust the function definition to use // instead of / we can force an integers at the output
def convert(inputValue, inputMin, inputMax, outputMin, outputMax):
outputVal = (inputValue - inputMin) * (outputMax - outputMin) // (inputMax - inputMin) + outputMin
return outputVal
Test one more time to proove integer output:
for i in range(10):
print(i, convert(i, 0, 10, 0, 500))
Wire up an Analog Input and PWM Output as shown below

And let's run the following on our ESP32
# analog in to pwm out
from machine import ADC, Pin, PWM
from time import sleep_ms
ledPin = Pin(27)
potPin = Pin(34)
pot = ADC(potPin)
pot.atten(ADC.ATTN_11DB)
pot.width(ADC.WIDTH_10BIT) # 0 - 1023
pwm = PWM(ledPin, freq=20000, duty=0)
while True:
sensor_val = pot.read()
print(sensor_val)
pwm.duty(sensor_val)
sleep_ms(20)
Now, let's add the convert function and rewrite the above so a higher Analog Input value results in a lower PWM duty cycle
# analog in to pwm out
from machine import ADC, Pin, PWM
from time import sleep_ms
def convert(inputVal, inputMin, inputMax, outputMin, outputMax):
outputVal = (inputVal - inputMin) * (outputMax - outputMin) // (inputMax - inputMin) + outputMin
return outputVal
ledPin = Pin(33)
potPin = Pin(34)
pot = ADC(potPin)
pot.atten(ADC.ATTN_11DB)
pot.width(ADC.WIDTH_10BIT) # 0 - 1023
pwm = PWM(ledPin, freq=20000, duty=0)
while True:
sensorVal = pot.read()
mapVal = convert(sensorVal, 0, 1023, 1023, 0)
print(sensorVal, mapVal)
pwm.duty(mapVal)
sleep_ms(20)
Add a second LED to your breadboard and try the following, in which one Analog Input controls two PWM Outputs in opposite orientations
# analog in to pwm out
from machine import ADC, Pin, PWM
from time import sleep_ms
def convert(inputVal, inputMin, inputMax, outputMin, outputMax):
outputVal = (inputVal - inputMin) * (outputMax - outputMin) // (inputMax - inputMin) + outputMin
return outputVal
led1Pin = Pin(27)
led2Pin = Pin(33)
potPin = Pin(34)
pot = ADC(potPin)
pot.atten(ADC.ATTN_11DB)
pot.width(ADC.WIDTH_10BIT) # 0 - 1023
pwm1 = PWM(led1Pin, freq=20000, duty=0)
pwm2 = PWM(led2Pin, freq=20000, duty=1023)
while True:
sensorVal = pot.read()
mapVal = convert(sensorVal, 0, 1023, 1023, 0)
print(sensorVal, mapVal)
pwm1.duty(sensorVal)
pwm2.duty(mapVal)
sleep_ms(20)