Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Matlab Arduino-MATLAB communication using SerialEvent() for PID Loop

  1. Oct 28, 2016 #1
    Hello,

    I have an arduino code for driving a peltier pile to a given set point (TEMP_SP). I am trying to input the set temperature from Matlab GUI (with different protocols, user will enter n temperatures and the time intervals, arduino will implement the PID controller and output the temperature to MATLAB every 1 seconds via serial communication.

    I have a serious problem and I would appreciate any help.

    My PID is working with arduino, I can send and receive data to/from matlab/arduino. I am using SerialEvent() to read the messages from matlab and print the responses accordingly.

    The problem is that, (maybe because there is a huge delay between matlab and arduino), when I set a temperature from matlab, my peltier temperature jumps up and down crazily. E.g. I set 20 degrees celcius, my peltier jumps up to 35-40 degrees, then toggles around lower temperatures that are not super related to my set temperature. This does not happen when I run the PID on arduino and set the temperature directly from there.

    PS: I am reading the temperature using a thermistor. I am changing the voltage using an external DAC and a shifter circuit because I need to both heat up and cool down ( the peltier terminals should change signs)

    Algorithm:
    -Set temperature (TEMP_SP)
    -Read thermistor voltage-->convert to resistance-->convert to temperature (this is current state, T_C_CS)
    -Find DAC output from PID
    -SerialEvent() runs after every loop() to check if there's message coming from matlab and sets its flag accordingly.
    -In each loop, check stringComplete flag and change the set temperature or print the current temperature.




    This is my Arduino code:
    Code (C):

    #include <Wire.h>
    #include <Adafruit_MCP4725.h>
    #include <PID_v1.h>
    Adafruit_MCP4725 dac; // constructor


    uint32_t dac_value;


    // Thermistor variables
    float Rref=10000; //ohms, resistance of thermistor at 25C
    float B25=3977;
    float TOLB=0.75;
    float A=-14.6337;
    float B=4791.842;
    float C=-115334;
    float D=-3.730535E+06;
    float A1t=3.354016E-03;
    float B1t=2.569850E-04;
    float C1t=2.620131E-06;
    float D1t=6.383091E-08;
    float Vdd=5; //volts
    float R_divider=10000;//ohms

    //CURRENT STATE
    float Vt_CS_val; //current thermistor voltage value
    float Vt_CS; //current thermistor voltage
    float Rt_CS;//current thermistor resistance
    float T_K_CS;  //current temperature in K
    float T_C_CS;  //current temperature in C
    float input_CS_val;// current input from the DAC
    float input_CS;// current input from the DAC

    //SET POINT
    float TEMP_SP=20; //desired temperature (celcius)
    float TEMP_SP_K; //desired temperature (Kelvin)
    float R_SP; //desired resistance for the TEMP_SP_K
    float Vt_SP; //desired thermistor voltage


    //DAC output
    float DAC_val;
    float DAC_OUT;//DAC output voltage
    //double PID_OUT;


    //Custom PID
    float der = 0;
    float err;
    float err_p;
    float integral;

    //PID set
    float Kp=1.0, Ki=0.003, Kd=10.0;
    float output;
    int state=1;

    float read_voltage;
    float temp_rec;
    float temp_new=20;


    String inputString = "";         // a string to hold incoming data

    String READ = "R";
    boolean stringComplete = false;  // whether the string is complete


    void setup() {
        Serial.begin(9600);
     
         dac.begin(0x62); // The I2C Address: Run the I2C Scanner if you're not sure
         DAC_val=2080;//2.54V
         dac.setVoltage(DAC_val, false); //819:1V,  3276:4V
     
        // reserve 200 bytes for the inputString:
        inputString.reserve(200);
     
        Serial.println('a');
        char a = 'b';
        while(a != 'a')
        {
          a = Serial.read();
        }
    }

    void loop() {
     

                Vt_CS_val = analogRead(A1);
                Vt_CS=5.0*Vt_CS_val/1024.0; //current voltage on thermistor, current temp
                Rt_CS=R_divider*Vt_CS/(Vdd-Vt_CS);
             
           
                //Given R, find T
                T_K_CS=pow((A1t+B1t*log(Rt_CS/Rref)+C1t*pow((log(Rt_CS/Rref)),2)+D1t*pow((log(Rt_CS/Rref)),3)),-1); //KELVIN
                T_C_CS=T_K_CS-273.15; //current Celcius
         
                ///////PROCESS SET POINT
       
                TEMP_SP_K=TEMP_SP+273.15; //Celcius to K
                //Given T_SP, find R_SP
                R_SP=Rref*exp(A+B/TEMP_SP_K+C/pow(TEMP_SP_K,2)+D/pow(TEMP_SP_K,3)); // R_SP
             
                // find Vt_SP
             
                Vt_SP=R_SP*Vdd/(R_SP+R_divider); //Set voltage for thermistor divider. Aim: Vt_SP=Vt_CS
             
                err = Vt_SP - Vt_CS;
                integral = integral + err;
             
                output = Kp * err + (Ki * integral) + (Kd * der );
                err_p = err;
             
                DAC_OUT = -output + 2.5;
         
                 if (DAC_OUT< 1)
                 DAC_OUT = 1;
                 if (DAC_OUT > 4)
                 DAC_OUT = 4;
               
                 DAC_val=4096*DAC_OUT/5;

                 dac.setVoltage((int)DAC_val, false);

                // print the answer when message arrives:

                if (stringComplete) {
               
                  inputString.trim(); //remove newline characters or blanks
                             
                  if( inputString==READ){
                 
                      Serial.println(T_C_CS); //print the current temp
                 
                  }
                  else //number came, take as temperature
                  {
                    temp_new=inputString.toFloat(); //set new temperature

                    TEMP_SP=temp_new;

                    Serial.println(TEMP_SP); //send the set temp back
               
                  }
                      inputString = "";
                      stringComplete = false;
                }
    }


    void serialEvent() {
     
      while (Serial.available()) {
        // get the new byte:
        char inChar = (char)Serial.read();
        // add it to the inputString:
        inputString += inChar;
        // if the incoming character is a newline, set a flag
        // so the main loop can do something about it:
        if (inChar == '\n') {
          stringComplete = true;
        }
      }
    }
     
    Here is my matlab code:
    Code (Matlab M):

    %open serial object (working)

    delete(instrfind({'Port'},{'COM11'}));%clear the port
    %global ard;
    if(~exist('serialFlag','var'))
        [ard,serialFlag] = setupSerial('COM11');
    end
    disp('Connection successful');

    isvalid=isvalid(ard);

    %send set temperature (I am getting 28.00 back as TEMP_SP, so working)
    x=28;
    fprintf(ard,num2str(x));
    msg=fscanf(ard);
    disp(msg);

    %read the temperature back every 3 seconds
    while(1)
     
        fprintf(ard,'R');
        msg=fscanf(ard);
        disp(msg);
        pause(3);
    end
     
     
    Last edited by a moderator: Apr 28, 2017
  2. jcsd
  3. Nov 2, 2016 #2
    Thanks for the thread! This is an automated courtesy bump. Sorry you aren't generating responses at the moment. Do you have any further information, come to any new conclusions or is it possible to reword the post? The more details the better.
     
  4. Nov 3, 2016 #3

    Mark44

    Staff: Mentor

    Some thoughts, for what they're worth -- I don't have any experience working with Arduino.

    You could speed up your Arduino code be replaced the calls to pow().
    Code (C):

    T_K_CS=pow((A1t+B1t*log(Rt_CS/Rref)+C1t*pow((log(Rt_CS/Rref)),2)+D1t*pow((log(Rt_CS/Rref)),3)),-1); //KELVIN
    .
    .
    .
    R_SP=Rref*exp(A+B/TEMP_SP_K+C/pow(TEMP_SP_K,2)+D/pow(TEMP_SP_K,3)); // R_SP  
    I count 5 calls to pow() in the two lines above. In all of them you are either squaring a number or cubing it. Calls to pow() are much more expensive in terms of processor time than is ordinary multiplication.

    For example, instead of doing this: result = pow(x, 3)
    I would do this: result = x * x * x

    I would also split both of the lines above into maybe five or six lines, using temp variables. Having such complicated calculations makes debugging a lot harder.

    Also in the Arduino code you have this:
    Code (C):
    while (Serial.available()) {
        // get the new byte:
        char inChar = (char)Serial.read();
        // add it to the inputString:
        inputString += inChar;
        // if the incoming character is a newline, set a flag
        // so the main loop can do something about it:
        if (inChar == '\n') {
          stringComplete = true;
        }
      }
     
    Instead of reading one byte at a time using Serial.read(), could you read an entire line all at once? I'm guessing that there is a readln() function. Of course that would mean you would have to parse the line for its contents.

    Finally, in your matlab code
    Code (Matlab M):

    %read the temperature back every 3 seconds
    while(1)
       
         fprintf(ard,'R');
         msg=fscanf(ard);
         disp(msg);
         pause(3);
    end[/quote]
    Would it help the read the temperature more often? Three seconds is a very long time in terms of CPU operations. Your matlab code appears to be a message loop, reading messages and then dispatching them. If it is controlling what's happening in the Arduino, making it wait 3 seconds is probably way too long. Possibly you want the message loop to pause only a small fraction of a second (like a millisecond or maybe less), not 3 whole seconds.

    Hope this helps.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook

Have something to add?
Draft saved Draft deleted



Similar Discussions: Arduino-MATLAB communication using SerialEvent() for PID Loop
Loading...