Pid-relayoutput.ino

Hello
I installed the pid-relayoutput.ino firmware present
in the pid library, I connected a potentiometer to the input to simulate an analog signal, to pin D0 I have a circuit where an optocoupler combined with a relay is connected. I can’t understand how it works. If I simulate with the Tinker application the relay works and opens and closes the contacts.
Someone can give me more info on this firmware (attached).
Valentino

#include "pid.h"
#define RelayPin 2

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,5,1, PID::DIRECT);

int WindowSize = 100;
unsigned long windowStartTime;
void setup()
{
  Serial.begin(9600);
  windowStartTime = millis();
  
  //initialize the variables we're linked to
  Setpoint = 100;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(PID::AUTOMATIC);
}

void loop()
{
  Input = analogRead(1);
  myPID.Compute();

  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  if(millis() - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if(Output < millis() - windowStartTime) digitalWrite(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);
  Serial.print("ValInput: ");Serial.println(Input);
    delay(1000);
}

If you want to read analog signals you should use an A# pin

Also, even if you had the signal connected to D0 why are you not using D0 in this?

Hello @ScruffR first time I use I have do A1 and D0 but the firmware don’t work like regulator - controlling.
You write:Also, even if you had the signal connected to D0 why are you not using D0 in this?
as I know temperature control systems there is an input parameter (temp) the specific SW commands and regulates through the firmware the opening and closing of the relay to increase or decrease the temperature.
Best Valentino

Your code is analogRead() ing pin 1 (aka D1) but controlling RelayPin 2 (aka D2) via digitaWrite().

Since your verbal description of the issue neither mention D1 nor D2 but only D0 - which your code is not using anywhere - it’s rather unclear what and why that code looks as it looks.

1 Like

I apologize I made a writing mistake in my code
there is (#define RelayPin 2) where a relay is connected and the input is A1
(Input = analogRead (1), I checked with Serial.print and even omitting A in the print I see the values change.

If you insist

Hello
first of all I apologize, I have great respect for all of you who give valid support to the community.
For this I summarize what has already been written also attaching the firmware again.
So I connected a potentiometer to A1 to simulate an analog input signal to pin D2 a relay is connected via optocoupler.
If the entry signal varies, I suggest that the relay should open and close the contacts. I tested output D2 via Tinker and the relay works. In the skecht I inserted a Serial.print to verify that the input signal varies by acting on the potentiometer, as well as I put the display of the output value of D2.
Best Regards
Valentino

#include "pid.h"
#define RelayPin D2

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,5,1, PID::DIRECT);

int WindowSize = 5000;
unsigned long windowStartTime;
void setup()
{
  Serial.begin(9600);
  windowStartTime = millis();
  
  //initialize the variables we're linked to
  Setpoint = 100;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(PID::AUTOMATIC);
}

void loop()
{
  Input = analogRead(A1);
  myPID.Compute();

  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  if(millis() - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if(Output < millis() - windowStartTime) digitalWrite(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);
  Serial.print("ValInput: ");Serial.println(Input);
    delay(1000);
}

Please remove this delay() !

All PID regulator members are time based.
how do you expect that his is gonna work ? when you block entire PID for 1s at every loop ?
also your setpoint is set to 100
and your analogRead() is from 0 to 4095 (from 0 to 3.3V respectively)
so consider to use map() functionality as when you connect just a pot. to A1 and 3.3V
the value of 100 will be reached right after you just touch the pot.

try something like this:

Input = analogRead(A1);
Input = map(Input, 0, 4095, 0, 100);
myPID.Compute();

Best,

2 Likes

Hello @dreamER
I made the changes that suggested, due to the variable declarations it throws an error.

Input = map (Input, 0, 4095, 0, 100);

call of overloaded 'map (double &, int, int, int, int)' is ambiguous

With my limited knowledge I made the following modification as follows:
On top

double val0 = 0;
double val1 = 4095;
double val3 = 100;

On void loop()

Input = analogRead (A1);
Input = map (Input, val0, val1, val0, val3);
myPID.Compute ();

but it doesn't work the same, I also removed the delay as indicated, could you tell me which changes to make so that it works?
Best Valentino

Good morning @ScruffR
why does your answer remain in edit?
Thanks Valentino

What do you mean?

If you are referring to a notification that your post was edited, then this comes from my re-formatting of your posts.
You keep wrapping your verbal part of the post in a pre-formatted text block which isn’t meant for that but for code blocks. Hence I keep moving the verbal part of your posts out of that block.

When having it inside the block your @ pings will not work nor word wrapping work - among other things like “misguided” text formats.

There are two versions of map(): One takes all integers and the other all doubles. Since you are passing in a mix of that the compiler doesn't know which to take.
To solve that issue, you need to clarify either as

  Input = map((int)Input, 0, 4095, 0, 100);

or

  Input = map(Input, 0.0, 4095.0, 0.0, 100.0);

(adding the .0 makes clear to the compiler that the number literals are to be taken as floating point values)
However, I'd write that part like this anyhow

  Input = map(analogRead(A1), 0, 4095, 0, 100);

BTW, can you tell us, how you are testing whether the relay gets controlled correctly or not?
How are you "twiddling" the potentiometer?
With a setpoint of 100 and a max value of 100 you still haven't got a lot of wiggle room.
Without map() you only had very little wiggle room on the low end of the potentiometer and with it but both values at the top end you have no wiggle room at all at the top.

The former version probably had the relay mostly off and the current version mostly/always on I'd suspect.

Another code streamline would be this
instead of

  if(Output < millis() - windowStartTime) digitalWrite(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);
  Serial.print("ValInput: ");Serial.println(Input);

I'd write

  digitalWrite(RelayPin, millis() - windowStartTime > Output);
  Serial.printf("In: %6.0f, Out: %6.0f, time: %6d \r", Input, Output,  millis() - windowStartTime);

The extra data in the Serial.print() statement should also provide some more insight into what's going on :wink:

You are also missing pinMode(RelayPin, OUTPUT) in setup().


Here is some code to try

#include "pid.h"

const int      pinRelay       = D7;     // use onboard LED as visual feedback for the relay state
const int      pinTempSensor  = A0;     // potentiometer pin
const uint32_t msTimeFrame    = 5000;

bool     localRead            = true;   // take reading from potentiometer
uint32_t msStart;

double setPoint;
double input;
double output;
PID myPID(&input, &output, &setPoint, 2, 5, 1, PID::DIRECT);

void setup() {
  Particle.function("newSetPoint", newSetPoint);
  Particle.function("newTemp", newTemp);

  pinMode(pinRelay, OUTPUT);
  pinMode(pinTempSensor, INPUT);
  
  setPoint = 
  input    = 100;                       // start off mid range and on target
  msStart  = millis();

  myPID.SetOutputLimits(0, msTimeFrame);
  myPID.SetMode(PID::AUTOMATIC);
}

void loop() {
  if (localRead)                        // when required, read potentiometer
    input = map(analogRead(pinTempSensor), 0, 4095, 0, 200);

  myPID.Compute();

  if(millis() - msStart > msTimeFrame)  // reached end of time frame
    msStart += msTimeFrame;             // shift forward

  digitalWrite(pinRelay, (output < (millis() - msStart)));
  Serial.printf("In: %6.0f, Out: %6.0f, time: %6d %c \r", input, output,  millis() - msStart, digitalRead(pinRelay) ? '+' : '-');
}


int newSetPoint(String arg) {
  setPoint = arg.toFloat();
  return setPoint;
}

int newTemp(String arg) {
  input = arg.toFloat();                
  localRead = (input <= 0);             // values <= 0 replace remote value with local potentiometer reading
  return input;
}

Here you can set the setPoint via a Particle.function().
The current state (input) can either be set remotely via another Particle.function() or via the potentiometer on A0 when you set the remote input to anything less or equal to zero (= initial condition).
The relay goes to D7 where the onboard LED also indicates the output state.

To view the serial output I'd recommend particle serial monitor --follow to see a steady line permanently disclosing the internal state of the program.

1 Like

Hello @ScruffR thanks for the suggestion, soon I’ll try then until tomorrow it will not be possible for me to run tests and leave any feedback in the community.
Good day
Valentino

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.