Math, relating to Arduino code

Ok its maths but it is going to be used in my code.
My code outputs a calculated value based on the spindle speed string variable, this output is a value between 0 and 7500 rpm

The excel data is as follows
The command being sent is in column A and the measured RPM is in column B
A is what i'm after the output to be.
The output signal is 0-10v derived from a 12 bit DAC and amplifier circuit. (The DACs output is 0-5v over the 12 bit range)
I'm currently treating the data as being linear and multiplying the RPM command by 0.589 effectively halving the full RPM of 7500 and adding a slight amplification value of 0.089 so that it can be output via the DAC

How can i get the values to be more accurate and create a formula that will give the correct output for given input.

miller speed best fit.pdf (183 KB)

new 1.txt (1.13 KB)

You could fit the data to a straight line that does not pass through the origin. That works much better than your present approach.

lin_fit.png

lin_fit.png

Thanks for the reply

I created the scatter graph and managed to produce the same trend line as you have kindly shown.

My next problem is understanding how to use the equation

I put an x value into a cell and the equation into another

so 200 in cell (I11) and equation into (J11) 1.0608*I11 - 126.67 which gives me 85.49 which is similar to the value i'm currently getting

If I transmit 200 how can i manipulate the formula to get 200 out.

From the speeds im receiving up to 2400 RPM is reading blow what it should be and above 2400 RPM is reading higher than it should.

does not pass through the origin

I don't quite understand what this means

I have attached an image of the trend line settings for you to confirm what i'm doing

I don't quite understand what this means

The equation you used in the OP is

y = 0.589*x

That is the equation of a line that passes through the origin (of the coordinate system), meaning that when x=0, y=0.

In this equation the line does not pass through the origin:

y = 1.0608*x - 126.67

when x=0, y = -126.67

If I transmit 200 how can i manipulate the formula to get 200 out.

You can't, because of errors in the data. Using any formula, you can get only an approximation to the expected value.

If you want the exact values in the table you posted, use that table as a "lookup table" instead of an equation that only approximates the trend.

A colegue has looked at my data and come up with an equation which only has a 5 RPM error.

its a second order polynomial

y = 0.000000002746 x^3 - 0.000025778517 x^2 + 0.614445568581 x + 46.160953172061

how can i use the equation in my code

Something like ?

void InverterOutput () 
{

    
// RPM

        ch1= FS_SpeedMsg; // Spindle speed value fom incomming message  

        ch1=(0.000000002746*(FS_SpeedMsg)^3 - 0.000025778517*(FS_SpeedMsg)^2 + 0.614445568581* (FS_SpeedMsg) + 46.160953172061
}

The full working code without this equation as it is now

//============================================
// VFD_FO_Interface
// 30th January 2019
//
#include <SoftwareSerial.h>
#define rx 2
#define tx 3
#include <Wire.h>
#include <Adafruit_MCP4725.h>
      
Adafruit_MCP4725 dac;
SoftwareSerial XSERIAL =  SoftwareSerial(rx, tx, false);

const byte numChars = 10;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

      // variables to hold the parsed data
char messageFromPC[numChars] = {0};
int SpindleSpd = 0;
int integerFromPC = 0;
float floatFromPC = 0.0;

boolean newData = false;

// Mechanical states //////////////////////////////////////////////////////////
      
const int SudsPump =  7;
const int SpinFWD =  5;
const int SpinREV =  6;

// Incoming messages  /////////////////////////////////////////////////////////
const unsigned int MAX_INPUT = 10;
long int FS_SpeedMsg = 0;  // spindle speed

int CmdSpindleCW = 0; // spindle CW direction
int CmdSpindleCCW = 0; // spindle CCW direction
int CmdCoolant = 0;   // activate suds pump
int data  = 0;        // Spare data block

// Variables will change: ////////////////////////////////////////////////////

int SudsPumpState = LOW;
int SpinFWDState = LOW;
int SpinREVState = LOW;
int dummy;

bool Fwd_Rev;
float ch1;//spindle pwm
//int sensorValue = 100;        // value read from the pot
int outputValue = 0;        // value output to the PWM (analog out)

//============

void setup() {
  
    Serial.begin(9600);
    XSERIAL.begin(2400);
 dac.begin(0x60);
    
      
        // initialize digital output.
        pinMode(SudsPump, OUTPUT);
        pinMode(SpinFWD, OUTPUT);
        pinMode(SpinREV, OUTPUT);

}

//============

void loop() {
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        showParsedData();
        newData = false;
    }
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (XSERIAL.available() > 0 && newData == false) {
        rc = XSERIAL.read();
//Serial.println(rc);
        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC

    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    integerFromPC = atoi(strtokIndx);     // convert this part to an integer

   // strtokIndx = strtok(NULL, ",");
   // floatFromPC = atof(strtokIndx);     // convert this part to a float

}

//============

void showParsedData() {
SpindleSpd = atoi(messageFromPC);
FS_SpeedMsg = SpindleSpd;  
    Serial.print(SpindleSpd);
    //Serial.print(messageFromPC);
    Serial.print(", ");

    Serial.println(integerFromPC);
    Serial.println(ch1);


   CmdSpindleCW = (bitRead (integerFromPC,0)); //Spindle CW Directrion
    CmdSpindleCCW = (bitRead (integerFromPC,1));
    CmdCoolant = (bitRead (integerFromPC,2));
    
    if (FS_SpeedMsg == 0)
    {
     CmdSpindleCW = 0;
     CmdSpindleCCW = 0; 
    }

    InverterOutput (); // Send data to variable frequency drive
    
}

void InverterOutput () 
{

    
// RPM

        ch1= FS_SpeedMsg; // Spindle speed value fom incomming message  

        dac.setVoltage((ch1 * 0.589), false); //17~4095 full range Generates 4.1 x ch to give 4095 max o/p
       


// Direction Relay Output Conditions       
      
        // 10 is minimum RPM - if a value less than 10 is sent the spindle will shut off
        if (ch1 > 10) {
        //Fwd_Rev = CmdSpindleCW; // direction value fom incomming RS485 message
      
          SpinFWDState = CmdSpindleCW;
          SpinREVState = CmdSpindleCCW;
          //Serial.println (SpinFWDState);
          //Serial.println (SpinREVState);
      
        }
        else {
          SpinFWDState = 0;
          SpinREVState = 0;
        }
      
// Coolant Machine Commands
      
      SudsPumpState = CmdCoolant;// value fom incomming message
      
        if (SudsPumpState == 1) {
          digitalWrite(SudsPump, HIGH);   // turn the LED on (HIGH is the voltage level)
      
        }
        else {
          //Energise the reset relay
      
          digitalWrite(SudsPump, LOW);    // turn the LED off by making the voltage LOW
          //                //delay by so many m/s then
          //                //deenergise reset relay
        }

// Direction Relay Outputs
        
        //
        if (SpinFWDState == 1) {
          //
          digitalWrite(SpinFWD, HIGH);   // turn the LED on (HIGH is the voltage level)
        }
        else {
          digitalWrite(SpinFWD, LOW);    // turn the LED off by making the voltage LOW
          //
        }
        //
        if (SpinREVState == 1) {
          digitalWrite(SpinREV, HIGH);   // turn the LED on (HIGH is the voltage level)
        }
        else {
          digitalWrite(SpinREV, LOW);    // turn the LED off by making the voltage LOW
          //
        }  
}

Im thinking replace x in formula with FS_SpeedMsg

0.000000002746* (FS_SpeedMsg)^3 - 0.000025778517*(FS_SpeedMsg)^2 + 0.614445568581* (FS_SpeedMsg) + 46.160953172061

got lovely errors about double conversion to double etc.
any thoughts

In C/C++ the "^" symbol denotes exclusive OR, not powers.

To compute simple powers of x, use multiply.

y = 0.000000002746*x*x*x - 0.000025778517*x*x + 0.614445568581*x + 46.160953172061;

Note that Arduino floats are single precision only, and support no more than about 6-7 decimal digits, so 46.16095 is equivalent to the last constant above.

Am I best to build up the equation in steps then bring the steps together as one solution.
something like how you would concatenate ? for outputting serial data.

Will the arduino pass the solution to the left of this huge equation

should i be using long to keep the precision.

You have

0.000000002746* (FS_SpeedMsg)^3 - 
0.000025778517*(FS_SpeedMsg)^2 +
0.614445568581* (FS_SpeedMsg) + 
46.160953172061

For the first line, if you divide FS_SpeedMsg by 100 the cube will be divided by 1000000 so to counter this you have to multiply the constant by the same number leading to

0.002746* pow (FS_SpeedMsg/100, 3)

The same for the second line

 0.25778517*pow(FS_SpeedMsg/100, 2)

No need to change the rest.
(not tested but should work)

should i be using long to keep the precision.

No. Precision is not an issue in this case, because you are using a function to approximate the table values.

I went for the long sum method

y = 0.000000002746xxx - 0.000025778517xx + 0.614445568581x + 46.160953172061;

and it works perfectly
Its amazing what these little chips can do.

after 3 months ironing out an electronics bug (mainly emi and rf related) i needed to get the miller back up and running. for my own sanity more than anything else.

In the end the line was put through the output value to the DAC which made alot of sense as that was the result i wanted and not finding what the error was between input and output RPM - new eyes on the problem and all that.

but i suppose the Z80 was used in the controls of the first space flights, That's scares me a lot more. i can remember loading games to a ZX81 with a screaming audio tape only to find the program crashed at the end.
I would not have wished to go into space with that technology.

many thanks for you help guys
lesept, i will try your method as well, just cause i can. Its great to learn stuff

but i suppose the Z80 was used in the controls of the first space flights

No, the Z80 didn't go in sale until nearly twenty years after the first space flights (over 30 years, if you include the V-2)

I would not have wished to go into space with that technology.

People went into space when orbits were still being calculated using electromechanical calculators.

See the recent film "Hidden Figures" to learn more, especially that certain women did many of the really difficult calculations.

jremington:
In C/C++ the "^" symbol denotes exclusive OR, not powers.

To compute simple powers of x, use multiply.

y = 0.000000002746*x*x*x - 0.000025778517*x*x + 0.614445568581*x + 46.160953172061;

You can reduce the number of multiplications like so

y = ((0.000000002746*x - 0.000025778517)*x + 0.614445568581)*x + 46.160953172061;

Yes, using the Horner form of the polynomial is a good idea, but that seemed to be on a level a few steps higher than the question merits.