B4R Question Wrap AutoPid double *input in Constructor

KiloBravo

Active Member
Licensed User
B4X:
// Constructor - takes pointer inputs for control variales, so they are updated automatically
    AutoPID(double *input, double *setpoint, double *output, double outputMin, double outputMax, double Kp, double Ki, double Kd);
 // Allows manual adjustment of gains

C-like:
AutoPID::AutoPID(double *input, double *setpoint, double *output, double outputMin, double outputMax,
                 double Kp, double Ki, double Kd) {
  _input = input;
  _setpoint = setpoint;
  _output = output;
  _outputMin = outputMin;
  _outputMax = outputMax;
  setGains(Kp, Ki, Kd);
  _timeStep = 1000;
}//AutoPID::AutoPID

Still having issues wrapping my first library.

Do I do something like (ArrayDouble* arrInput = CreateStackMemoryObject(Input)) like the example below for for the Pointers to Doubles for input, setpoint, and output in my B4R::Initialize, since I can't expose pointers to Doubles in B4R ? Those three variables are updated as the PID object runs in the background.

B4X:
void MqttClient::callback(char* topic, uint8_t* payload, unsigned int length) {
        const UInt cp = B4R::StackMemory::cp;
        B4RString* strTopic = CreateStackMemoryObject(B4RString);
        strTopic->data = topic;
        
        ArrayByte* arrPayload = CreateStackMemoryObject(ArrayByte);
        arrPayload->data = payload;
        arrPayload->length = length;
        
        instance->MessageArrivedSub(strTopic, arrPayload);
        B4R::StackMemory::cp = cp;
//
 

Johan Schoeman

Expert
Licensed User
Longtime User
I am aware this is an old thread but could not find the "answer" to the question in post #1 above. Attached is my B4R example project using inline C.

You need to extract AutoPID and add it to your Arduino library folder.

I ran into an issue with integral windup / winddown (took me some time to figure it out) and had to limit the integral sum in the B4R code. Other than that, it seems to be working well.

I have a LED connect to D0 facing directly at a LRD (output via PWM)
A Pot with wiper connected to A1 (setpoint)
LDR voltage divider going to A0 (feedback)
A 24VDC fan connected to D1 (PWM controlled)

You can replace the 24VDC fan with a LED/resistor pair (I am using a 24V Controllino Maxi - equivalent to Arduino Mega but industrial type PLC)

B4R Code:
#Region Project Attributes
    #AutoFlushLogs: True
    #CheckArrayBounds: True
    #StackBufferSize: 300
#End Region
'Ctrl+Click to open the C code folder: ide://run?File=%WINDIR%\System32\explorer.exe&Args=%PROJECT%\Objects\Src

Sub Process_Globals
    Public Serial1 As Serial
    
    Dim t As Timer
    
    Dim kp, ki, kd As Double                 'Used to set the kp, ki, kd parameters in the inline C code
    Dim integral As Double

    Dim cont As CONTROLLINO
    
    Dim setpotpin, feedbackpin, outputpin, additionaloutputpin As Pin
    
    Dim A  As Long = 1000                    'Resistance in darkness in KΩ
    Dim B As Int = 15                        'Light resistance (10 Lux) in KΩ
    Dim Rc As Int = 10                       'Calibration resistance in KΩ
    
    Dim input, setpoint, output As Double
    Dim feedbackval As Float
    
    Dim outputMin, outputMax As Double
    
    Dim threshold As Double = 3              'threshold between Setpoint and Feedback to stop doing PID corrections
    
    Dim atSetpoint, isStopped As Boolean      'true when Setpoint and Feedback is within +- the threshold range
    
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    integral = 0
    
    outputMin = 0
    outputMax = 255
        
    setpotpin.Initialize(cont.CONTROLLINO_A1, setpotpin.MODE_INPUT)                       'pin 55
    feedbackpin.Initialize(cont.CONTROLLINO_A0, feedbackpin.MODE_INPUT)                   'pin 54
    outputpin.Initialize(cont.CONTROLLINO_D0, outputpin.MODE_OUTPUT)                      'pin 2
    additionaloutputpin.Initialize(cont.CONTROLLINO_D1, additionaloutputpin.MODE_OUTPUT)  'pin 3
    
    t.Initialize("t_tick", 20)   
    
    kp = 0.4                    'the kp constant of the PID controller
    ki = 6.0                    'the ki constant of the PID controller
    kd = 0.0001                 'the kd constant of the PID controller
    
    RunNative("setup", Null)
    RunNative("setOutputRange", Null)
    RunNative("setGains", Array(kp, ki, kd))

    t.Enabled = True
    
End Sub

Sub t_tick
    setpoint = setpotpin.AnalogRead/4
    feedbackval = readFeedback
    
    RunNative("loop", Null)
    RunNative("getIntegral", Null)          'get the sum of the integral
    If integral > 60 Then                   'limit the winding up of the integral
        RunNative("setIntegral", 60)        'set the integral wind up to max 60
    else if integral < 0 Then
        RunNative("setIntegral", 0)         'set the integral wind down to min 0
    End If

    Log("Setpoint = ", setpoint)
    Log("Feedback = ", feedbackval)
    Log("Output = ", output)
    Log("integral = ", integral)
    RunNative("atSetPoint", threshold)
    Log("atSetPoint = ", atSetpoint)
    RunNative("isStopped", Null)
    Log("isStopped = ", isStopped)
    
    Log(" ")
    
    outputpin.AnalogWrite(output)
    additionaloutputpin.AnalogWrite(output)
    
End Sub


Sub readFeedback As Float                    'specific to the LDR I am using as feedback from the "plant"
    
    Dim mean As Float = 0
    Dim V As Float
    For i = 0 To 99
        V = feedbackpin.AnalogRead
        mean = mean +  (V * A * 10) / (B * Rc * (1024 - V))
    Next
    mean  = (mean / 100)
    Return mean
    
End Sub

#If C

#include <AutoPID.h>


#define FEEDBACK_READ_DELAY 10 //

//pid settings and gains
#define OUTPUT_MIN 0
#define OUTPUT_MAX 0
#define KP 0.0
#define KI 0.0
#define KD 0.0

double feedback, setPoint, outputVal;


//input/output variables passed by reference, so they are updated automatically
AutoPID myPID(&feedback, &setPoint, &outputVal, OUTPUT_MIN, OUTPUT_MAX, KP, KI, KD);

unsigned long lastFeedbackUpdate; //tracks clock time of last feedback update

bool updateFeedback() {
  if ((millis() - lastFeedbackUpdate) > FEEDBACK_READ_DELAY) {
    feedback = b4r_main::_feedbackval; //get feedback reading
    lastFeedbackUpdate = millis();
    feedback = b4r_main::_feedbackval; //get feedback reading              //request reading for next time
    return true;
  }
  return false;
}             

void loop(B4R::Object* o) {
  updateFeedback();
  setPoint = b4r_main::_setpoint;
  myPID.run(); //call every loop, updates automatically at certain time interval
  b4r_main::_output = outputVal;
}     

void setup(B4R::Object* o) {

    myPID.setTimeStep(50);         //updates automatically at certain time interval
}     

void setGains(B4R::Object* o) {

    myPID.setGains(b4r_main::_kp, b4r_main::_ki, b4r_main::_kd);
} 

void getIntegral(B4R::Object* o) {

     b4r_main::_integral = myPID.getIntegral();
} 

void setIntegral(B4R::Object* o) {

     myPID.setIntegral(o->toDouble());
}             
        
void setOutputRange(B4R::Object* o) {
    myPID.setOutputRange(b4r_main::_outputmin, b4r_main::_outputmax);
}

void atSetPoint(B4R::Object* o) {

    boolean asp = myPID.atSetPoint(o->toDouble());
    b4r_main::_atsetpoint = asp;
}


void stop(B4R::Object* o) {
    myPID.stop();
}
 
 
void reset(B4R::Object* o) {
    myPID.reset();
}


void isStopped(B4R::Object* o) {

    boolean isStopped = myPID.isStopped();
    b4r_main::_isstopped = isStopped;
}

#End If
 

Attachments

  • b4rAutoPIDInlineC.zip
    2.4 KB · Views: 92
  • AutoPID.zip
    5.2 KB · Views: 84
Upvote 0
Top