Arduino-MATLAB communication using SerialEvent() for PID Loop

troubled_grad
Messages
1
Reaction score
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 celsius, 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:
C:
#include <Wire.h>
#include <Adafruit_MCP4725.h>
#include <PID_v1.h>
Adafruit_MCP4725 dac; // constructoruint32_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 (celsius)
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 completevoid 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 celsius
     
            ///////PROCESS SET POINT
   
            TEMP_SP_K=TEMP_SP+273.15; //celsius 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:
Matlab:
%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:
  • Like
Likes   Reactions: berkeman
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().
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:
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
Matlab:
%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.