Servo as a pointer but using a non linear scale, help mapping

So Im new to the forum, and new to arduino. Great another newb...yeah...While I design and build full automotove chassis harnesses, this is all new to me and coding is not my strong point. But I digress...

I have thermometer project, analog using a small servo as the needle pointer. The scale is non linear for the temp reading and I cannot change that due to the nature of the project. Repurposing a 100 year old volt meter. Te scale starts at 0 and goes to 175, but 100 is in the middle. The scale ramps up quick from 0 to 80 within the first 20% of needle movement, then the rest of the scale goes from 80-175 with quite a bit of resolution. See the attached pic for the scale.

So used the code on adafruit for an analog servo thermometer, and I mostly understand how it works, and it does work, but it seems most basic servo codes give you a range you can set, but that maps in a linear fashion to the temperature map via the code. So you can set 0 deg on the servo to 0 deg F and 180 deg on the servo to 120 deg F, for example of course.

I want to be able to more precisley map the temp to points. 20 deg servo to 40F, 23 deg to 45F and so on to be somewhat accurate with the scale provided in the project. Thoughts on how I can do this? Is the servo even the right way to go? Would a moving coil movement be more appropriate? In that case I would still have to set some kind of non linear mapping for this project as well.

Sorry for the long post, if someone can point me in the direction for code, or a vid I can watch that would be sweel, thanks!

Servo is fine. What you need to do is derive a formula which connects the temperature reading input (0-1023) with the required servo angle. If you have that then it can certainly be coded. In the worst case you could do it as a lookup table but that shouldn't really be necessary.

Effectively you are looking to produce a customised map() function which is non-linear. Your particular scale looks to be condensed at both ends, widening towards the centre but it's too early in the morning here for me to guess at a formula.

Steve

Collect some more data by experiment telling you how many degrees correspond to what temperature.

With that data in mind, take a look at MultiMap.

Alternatively, use the experimental results and derive a formula - Excel's solver or similar can do the calc for you.

Oof, so very over my head. Guess I gotta do a bunch of reading and try to understand how the multi map function works. This seems to be exactly what I need to do. I'll start by making a map of degrees vs temp then go from there.

I'll start by making a map of degrees vs temp

Post a table of degrees versus temperature and we can find a function to fit it.

Will do, gonna be a few days though, I left all the parts at work lol.

Ok attached a PDF since I couldn't attach an XLS with the servo position in relation to the scale, or temp readings...So what so we think?

Temp vs. Servo Deg - Sheet1.pdf (22.3 KB)

That curve is well fit with a 4th order polynomial, but there would be many other possibilities.

If T=temperature (float variable), then the servo angle A:

float A = 140.94 -0.25543*T + 0.011764*T*T - 0.00024458*T*T*T + 1.0574E-6*T*T*T*T;

FIt.PNG

So that formula pretty much plots the temp value to the service position if I understand correctly?

I should be able to take the code I have then and use this in it's place I'm guessing instead of the map function

The line of code IS a mapping function, which calculates the servo angle from the temperature.

If you have a problem with the code, post it (using code tags) and tell us what goes wrong.

So ive been gone for a bit, stuff goin on. Finally getting back to this project. Im having issues figuring out how to get the line of code into the code im using. I hate to admit I have the hardest time wrapping my head around coding.

I know I have to set the min and max servo position with this code, but this uses the PWM time to determine servo position, im not sure how to figure that out. And the line of code, float A= etc etc. T is temperature, A is for servi angle I have to figure out how to make that obvious in the code. Im gonna sit down tonight or tomorrow and try to understand it better. Any help here would be greatly appreciated. I attached the code im using also. How do I post code like jremington did?

Thermometer_project1.ino (1.73 KB)

Use code tags to post code (</> button), as clearly described in the "How to use this forum" post.

So I swapped the line of code that would linearly control the servo for the line of code provided by jremington, changed A to the servoPos and T to temp to match the rest of the code, and it compiled no issues.

I haven't dumped it into the board yet, dont have it with me at work. But do you think that looks legit? Im wondering do I need both of the conversion codes at the end F to C and C to F. This is F only. And the PWM for servo object control. Im not exactly sure how those would effect it. I do need to limit servo movement which I am going to do physically inside. If it goes too far it will destroy the original needle, and id rather destroy a 5$ servo motor instead. But in addition, that code im guessing sets the boundaries for the servo?

#include <Servo.h>

#define ANALOGREFVOLTAGE 5.555
 
// TMP36 Pin 
const int tempPin = A0;

// create servo object to control the analog gauge
Servo metroServo;
// pulse width (uS) corresponding to 0 on the servo
const int servoMin = 544;
// pulse width (uS) corresponding to 180 on the servo
const int servoMax = 2400;

int servoPos = 0;

void setup() {
  // start the serial connection
  Serial.begin(9600);
  // attach a servo at pin 9 with movement constraints
  metroServo.attach(9, servoMin, servoMax);
}

void loop() {
  // read the voltage from the pin
  float voltage = getVoltage(tempPin);

  
  // convert the voltage to a temperature value
  float temp = convertToF(voltage);

  /* We're going to take in the Temperature and map it to a servo value:
   *  Minimum Temperature Value: -10 (you can change this!)
   *  Maximum Temperature Value: 100 (you can change this, too)
   *  If your temp is -10, the servo will move to 0 degrees
   *  also, if your temp is 100, the servo will move to 180 degrees
   *  ...everything else will be mapped between the range
   */
  
  /*original line of code*/
  //servoPos = map((int(temp)), 0, 100, 0, 180);

  float servoPos = 140.94 -0.25543*temp + 0.011764*temp*temp - 0.00024458*temp*temp*temp + 1.0574E-6*temp*temp*temp*temp;
  // write servoPos to the servo
  metroServo.write(servoPos);
  // poll every minute
  delay(1000);

}

// gets the voltage from the analog pin
float getVoltage(int pin) { 
  return(float(analogRead(pin))* float(ANALOGREFVOLTAGE/1023.000));  
}

// convert C to F
float convertToF(float voltage) {
  return (((voltage - .5) * 100)*1.8) + 32;
}

// convert F to C
float convertToC(float voltage) {
  return (voltage - 0.5) * 100;
}