Using squared math to map a timer to a digipot

Hello,

I have a working program that maps a digipot to a timer. I have been trying to create a curve to the mapping that I'm struggling with. With the math the digipot is either at 0 or 127 nothing in-between. the math is with int but returns numbers that have decimal places. that should just round to the nearest whole number wouldn't it?

digitalPot = constrain(temp, 127, 0); //stay within the 0-127 range

digitalpot = sq(digitalpot)/(sq(digitalpot)/127); //math

digitalpot = map(digitalpot, 127, 0, 0, 127); // swap back to correct digipot orentation

ds3502.setWiper(digitalPot);

#include <Adafruit_DS3502.h>                  // <-----<<<<<
Adafruit_DS3502 ds3502 = Adafruit_DS3502();   // <-----<<<<<
//the DS3502 can be set from 0 to 127 using I2C // <-----<<<<<


#define magnetDetected       HIGH
#define noMagnet             LOW

#define weAreTiming          true
#define weAreNotTiming       false


#define BRAKE_IN_PIN A0
const byte CLUTCH_IN_PIN    =A3; 
const byte POTL            = A1;  // Pot on Analog Pin 1
const byte POTH            = A2;  // Pot on Analog Pin 2

bool timerRunningFlag      = weAreNotTiming;

const byte sensor          = 2;
const byte heartbeatLED    = 13;

byte lastWheelState        = noMagnet;

int brake_value           = 0;
int clutch_value          = 0;
int clutch_temp           = 0;
int digitalPot                ;
int lowspeed               = 0;   // Low speed set
int highspeed              = 0;   // High Speed set
int lowrpm                 = 0;   // Low RPM kick in
int highrpm                = 0;   // High RPM kick in
int lowValue               = 0;
int highValue              = 0;
int digitalpot             =127;


const int lowLow           = 500; //reading 0
const int lowHigh          = 250; //reading 1023

const int highLow          = 251; //reading 0
const int highHigh         = 50; //reading 1023

//timing stuff
unsigned long startTime;
unsigned long endTime;
unsigned long duration;
unsigned long timerMillis;
unsigned long heartbeatMillis;
unsigned long wheelMillis;

//*********************************************************************
void setup()
{
  ds3502.setWiper(0);
  pinMode (sensor, INPUT_PULLUP);

  pinMode (heartbeatLED, OUTPUT);

  Serial.begin(9600);

  while (!Serial)
  {
    delay(1);
  }

  Serial.println("Adafruit DS3502 Test");

  if (!ds3502.begin())                             // <-----<<<<<
  { // <-----<<<<<
    Serial.println("Couldn't find DS3502 chip");   // <-----<<<<<
    while (1);                                     // <-----<<<<<
  }                                                // <-----<<<<<

  Serial.println("Found DS3502 chip");

} //END of setup()


//*********************************************************************
void loop()

{
  //***********************
  //time to toggle the heartbeatLED ?
  if(millis() - heartbeatMillis >= 500);
  {
    //restart the TIMER
    heartbeatMillis = millis();

    //toggle the LED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //***********************
  //time to check the wheel sensor ?
  if (millis() - wheelMillis >= 10)
  {
    //restart the TIMER
    wheelMillis = millis();
    checkWheelSensor();
  }

  //***********************
  //if we are timing, have we reached the upper timing limit ?
  if (timerRunningFlag == weAreTiming && millis() - timerMillis >= 1000)
  {
    //disable the timing
    timerRunningFlag = weAreNotTiming;

    duration = 1000;

    printDigitalPot();
  }

  //***********************
  //Other non-blocking code goes here
  //***********************

  while (brake_value < 900) {
    ds3502.setWiper(127);
    Serial.print("Brake on ");
    Serial.println(brake_value);
    brake_value = analogRead(BRAKE_IN_PIN);
  }

  while (clutch_value < 920 && brake_value > 900) {
    clutch_temp = map(clutch_value, 800, 80, digitalPot, 127);
    clutch_temp = constrain(clutch_temp, 0, 127);
    ds3502.setWiper(clutch_temp);
    Serial.print("Clutch on ");
    Serial.print(clutch_value);
    Serial.print("  Digitalpot value:  ");
    Serial.println(clutch_temp);
    brake_value = analogRead(BRAKE_IN_PIN);
    clutch_value = analogRead(CLUTCH_IN_PIN);
  }
    

} //END of loop()


//*********************************************************************
void checkWheelSensor()
{
  byte thisState = digitalRead(sensor);

  //***********************
  //was there a change in state ?
  if (lastWheelState != thisState)
  {
    //update to the new state
    lastWheelState = thisState;

    //***********************
    //if we are 'not' timing has the sensor detected the magnet ?
    if (timerRunningFlag == weAreNotTiming && thisState == magnetDetected)
    {
      //enable timing
      timerRunningFlag = weAreTiming;

      //start this TIMER
      startTime = millis();

      //start this TIMER
      timerMillis = millis();
    }

    //***********************
    if (timerRunningFlag == weAreTiming && thisState == noMagnet)
    {
      //disable the timing
      timerRunningFlag = weAreNotTiming;

      endTime = millis();

      duration = endTime - startTime;

      printDigitalPot();
    }

  } //END of  if(lastWheelState != thisState)

} //END of checkWheelSensor()


//*********************************************************************
void printDigitalPot()
{brake_value = analogRead(BRAKE_IN_PIN);
clutch_value = analogRead(CLUTCH_IN_PIN);
  
 
  
  //read manual pots, map to the required thresholds
  lowspeed = analogRead(POTL);  //0 to 1023
  lowValue = map(lowspeed, 0, 1000, lowLow, lowHigh);

  highspeed = analogRead(POTH); //0 to 1023
  highValue = map(highspeed, 0, 1000, highLow, highHigh);

  int temp = map( duration, highValue, lowValue, 127, 0);       // these have been swapped since previous programs to allow for math
  

  digitalPot = constrain(temp, 127, 0);   //stay within the 0-127 range
 
  digitalpot = sq(digitalpot)/(sq(digitalpot)/127);    //math

  digitalpot = map(digitalpot, 127, 0, 0, 127);               // swap back to correct digipot orentation
   
  ds3502.setWiper(digitalPot);

 
   
   
  Serial.print("LOW ");
  Serial.print(lowspeed);
  Serial.print(" Time ");
  Serial.print(lowValue);

  Serial.print("  ");

  Serial.print("HIGH ");
  Serial.print(highspeed);
  Serial.print(" Time  ");
  Serial.print(highValue);

  Serial.print ("  Time in millis = ");
  Serial.print (duration);
  Serial.print("   Digital pot set to: ");
  Serial.print(digitalPot);
  Serial.print("\t");
  Serial.print("Brake : ");
  Serial.print(brake_value);
  Serial.print("Clutch : ");
  Serial.println(clutch_value);
  Serial.println("");
  

Your description of the problem makes little to no sense.

Please describe what you actually want to do, and post examples of the output.

Mathematically, the result of this calculation is always 127 (except when "digitalpot" is zero).

  digitalpot = sq(digitalpot)/(sq(digitalpot)/127);    //math

What I am trying to do is map the digipot to more of a curve than linear.

using excel
(114^2)/(127^2/127) |114|102.3307087||
|115|104.1338583||
|116|105.9527559||
|117|107.7874016||
|118|109.6377953||
|119|111.503937||
|120|113.3858268||
|121|115.2834646||
|122|117.1968504||
|123|119.1259843||
|124|121.0708661||
|125|123.0314961||
|126|125.007874||
|127|127||

What sort of a curve? To write code, you must have an equation.

Thats kind of where i'm stuck. Im open to suggestions. I thought my Exponential equation of digipot^2)/(127^2/127) would work but clearly it doesn't

  1. This above is not what you programmed
  2. The above is equivalent to sq(digitpot)/127

You still haven't explained what you really want to do. Until you can express your idea in clear language, I doubt anyone can help.

With only 128 values between 0 and 127, might be easier to use a lookup table.

This is a visual representation of what I’m trying to do, I’m not too fussy on the exact curve but just need to get away from the linear shape

The drawing helps, but what are the three numbers and inconsistent comments on the horizontal axis supposed to mean?

The horizontal axis is the timer from a magnetic pickup. There is a potentiometer that sets the high value from 200-90 so the scale can range from 250-200 milliseconds to 250-90 milliseconds depending on where the potentiometer is set

So, on the horizontal axis the "low" value might be 250, and the "high" value might be 90?

And those would correspond to maybe 250 <-> 127 and 90 <-> 0 on the digitpot?

If you want an equation, you have to specify exact numbers, plus one more to determine the amount of bend.

The low value is always 250, the high valve varies anywhere between 200 and 90 depending on where the potentiometer is set. the code needs to read that variable and implement it into the equation. I don't know how to calculate the amount of bend i want yet but wouldn't that just be a number that can be changed in the program if needed? If you could show me an example i can probably figure it out from there.

You don't seem to grasp that actual numbers always have to be supplied.

Please answer the question: would "high" correspond to 127, and "low" correspond to 0 on the digitpot ALWAYS? If not, what is the exact correspondence?

If the timer is at the high reading the digipot is set to 0, when the timer is at 250 (low speed) the digipot is set to 127

Great, so you have two points (x1, y1) = (90, 127) and (x2, y2) = (250, 0)

A straight line through them gives midpoint (x3, y3) = (170, 63.5)

Say you want a smooth curve that passes through the midpoint a bit lower, for example, (x3, y3) = (170, 40))

Use "Lagrange interpolation" to find all the intermediate values from this formula:

Lagrange

Here is what that curve looks like:

As suggested above, you can make a table using Excel, for example, and put that in the Arduino program, rather than the equation for the Lagrange formula.

Are you able to help me with the look up table? I've been trying to read articles on here and get it figured out but I'm just lost to be honest

Rather than use a lookup table, it seems like it would be considerably simpler to reduce the equation that @anon94801460 gave in #15 to y=ax^2+bx+c

And if that algebra seems too daunting, you can plot your three points in Excel, add a 2nd order polynomial trendline, and "Display Equation on chart":

It is utterly trivial to write the Lagrange interpolation formula in a line of C.

Here it is, all variables are floats:

L = (x-x2)*(x-x3)*y1/(x1-x2)/(x1-x3)+(x-x1)*(x-x3)*y2/(-x1+x2)/(x2-x3)+(x-x1)*(x-x2)*y3/(-x1+x3)/(-x2+x3);

That equation looks to take ~350uS for a 16MHz AVR to compute. That's probably fine but if not I'd precalculate the values in Excel and store the values in PROGMEM. 128 values x sizeof(float) would be just 512 bytes and you could access a value in ~7uS.

If I was the OP and wasn't "too fussy about the exact shape" of the curve but wanted to "get away from the linear shape," I'd probably want to visualize my curve, so as to make some judgment about its shape, so I'd plot it in Excel. Writing the resulting trendline equation, in C++, would be even simpler than "utterly trivial." Not to mention less prone to typing errors. :slight_smile: