Arduino-MATLAB communication using SerialEvent() for PID Loop

In summary: PID calculations}In summary, the problem is that 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.
  • #1
troubled_grad
1
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:
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 (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 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 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:
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 berkeman
  • #3
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.
 

FAQ: Arduino-MATLAB communication using SerialEvent() for PID Loop

What is Arduino-MATLAB communication using SerialEvent() for PID Loop?

Arduino-MATLAB communication using SerialEvent() is a method for establishing communication between an Arduino microcontroller and MATLAB software. This allows for the exchange of data and commands between the two platforms, which can be useful for implementing a PID (Proportional-Integral-Derivative) control loop.

Why is SerialEvent() used for Arduino-MATLAB communication?

SerialEvent() is used because it is an interrupt-driven function that allows for non-blocking communication between the Arduino and MATLAB. This means that the Arduino can continue to run its main code while also receiving and sending data to MATLAB, making it an efficient method for real-time control applications.

How does the PID loop work in Arduino-MATLAB communication using SerialEvent()?

The PID loop works by continuously receiving real-time data from a sensor connected to the Arduino, sending it to MATLAB for processing, and then receiving a control signal from MATLAB to adjust the output of the sensor. This is done in a continuous loop, allowing for precise control of the sensor output based on the desired setpoint.

What are the benefits of using Arduino-MATLAB communication for PID control?

The benefits of using Arduino-MATLAB communication for PID control include real-time data processing and control, as well as the ability to implement complex control algorithms and make adjustments in real-time. This can be particularly useful for applications such as robotics, automation, and data acquisition.

Are there any limitations to using SerialEvent() for Arduino-MATLAB communication?

One limitation of using SerialEvent() is that it relies on a serial connection, which can be prone to interference and signal loss. Additionally, the speed of data transfer may be limited by the baud rate of the connection. It is important to carefully consider these factors when designing an Arduino-MATLAB communication system for PID control.

Back
Top