Transforms 101 - Controlling a servo with a potentiometer


Senior Member
Sometimes we need to take a series of values from a device connected to an analog input, modify those values and output them in a different fashion. One common example of this is reading a potentiometer and using that to position a servo. However, the series of values from the potentiometer will range from 0 to 255, while the servo output through servopos needs to range from 75 to 225. We need to transform the input data to meet output requirements. In this particular case the transformation is simple one. We need to change the slope and adjust the ‘y-intercept’ or, perform a translation. This is what is called an affine transformation.

Generally speaking for an affine transform of this nature, given a variable X, which has range between XL and XH that we wish to transform to another variable, Y, with a range given by YL to YH we must us the following formula:

Y=(YH-YL)/(XH-XL)*(X-XL)+YL (1)

Now, let’s say we have a PICAXE-08M2 with the wiper of a potentiometer attached to C.1 and a servo attached to C.4. We intend to read the value of the potentiometer into variable b0. Transform the data as required and put the result into b1. We will then use b1 to position the servo.

In our case, X is the variable b0, read from the ADC using the command readadc C.1, b9 and whose range is 0 to 255, and Y is the variable b1 whose range we shall call b1L to b1H, so we can simplify (1) to our 'general case' of:

b1=((b1H-b1L)/255)*b0+b1L (2)

Now, apply this to our scenario where we wish to control a servo, the range we need is b1L=75 and b1H=225. Plugging in these values to (2) we get:

b1=((225-75)/255)*b0+75 (3)

Given the limitation of the integer math and the order of operations imposed by PICAXE basic, we must rearrange (3) to become:

b1=150*b0/255+75 (4)

Which will provide the results we require to use a pot to control a servo.

Now for a little more theory. We can now formulate 2 general affine transformations that can be used in PICAXE basic. The first uses a fixed range that we calculate before hand and hard code into the program. This formula would be useful in a case like the one that started this thread. Given an spread of R=b0H-b0L in the range of b0, and a desired output range of b1L to b1H we have:

b1=[b1H-b1L]*b0/R+b1L, (5)

Where the term [b1H-b1L] is pre-calculated as an integer and coded into the program like we have already shown in (4). In that case R=255.

The second uses a programmable range determined at run-time. This would be useful in cases where the transformation would need to be adapted to changing conditions or to various output devices. Given a spread of the range of b0 (R from above) in b4, and a desired output range in variables b2 and b3 where b2 < b3, we have:

b1=b3-b2*b0/b4+b2 (6)

Code Example 1 - As in our text, to control a servo with a full-range potentiometer and using equation (5), where:
b1L = 75
b1H = 225

servo C.4,150 

	readadc C.1,b0
	servopos C.4,b1
Code example 2 &#8211; Using the more general form, again control a servo that needs only a limited motion to vary a control valve based on a potentiometer that tells us a float level in a liquid tank. The potentiometer readings vary between 0 and 150 and the servo must be positioned between 100 and 180. Then using equation (6) we have:

let b3 = 180
let b2 = 100
let b4 = 150
servo C.4,100 

	readadc C.1,b0
	servopos C.4,b1


Senior Member
one of the calculations i seem to use alot now for pot to servo or adjustable pause delay functions courtesy of goeytex is this one :

servo travel of 75-225, subtract our desired lowest value we want from the total value of the target range we are aiming to achieve so 75 from 225 leaves 150

divide 150 by 5 gives 30 and divide 255 by 5 gives 51

then add the lowest value offset we originally subtracted from the 225 which is 75 back onto the end of the final calc in the command line as below

so final calc would be b0*30/51 +75 which will give a servo value to output of 75 -225 according to the readadc values from 0-255

the divide by 5 can be changed depending on how big a number the initial * command gives before you divide by, for one of my recent calcs i had to use a word var and have the divide by 10 as the initial * part of the start of the calc returned a value of just over 60000 so had to ensure no overflow.

seems to work great for adjustable pauses using potentiometer on adc as well as the servo values.
thank you. I had to go through other threads like the matrix to find this one. But... I wish this site would separate official lessons from standard threads in some way. This is very valuable information