Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
// ************************************************
// PID
// ************************************************
#include <PID_v1.h>
#include <PID_AutoTune_v0.h>
byte ATuneModeRemember=2;
double input=80, output=50, setpoint=5;
double kp=2,ki=0.5,kd=2;
double kpmodel=1.5, taup=100, theta[50];
double outputStart=5;
double aTuneStep=50, aTuneNoise=1, aTuneStartValue=100;
unsigned int aTuneLookBack=20;
int outputindicate = 0;
int WindowSize = 5000;
unsigned long windowStartTime;
boolean tuning = false;
unsigned long modelTime, serialTime;
PID myPID(&input, &output, &setpoint,kp,ki,kd, DIRECT);
PID_ATune aTune(&input, &output);
boolean useSimulation = false; //set to false to connect to the real world
#define RelayPin 7 // Output Relay
// ************************************************
// LCD
// ************************************************
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
// ************************************************
// Temperature Sensor
// ************************************************
double offset = -2; // for correcting temperature
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 12 // One-Wire Temperature Sensor
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire); // arrays to hold device address
DeviceAddress tempSensor;
// ************************************************
// Rotary Encoder
// ************************************************
int inPin = 4; // the number of the input pin
int state = HIGH; // the current state of the output pin
int reading; // the current reading from the input pin
int previous = LOW; // the previous reading from the input pin
long time = 0; // the last time the output pin was toggled
long debounce = 200; // the debounce time, increase if the output flickers
const int PinCLK = 2; // Used for generating interrupts using CLK signal
const int PinDT = 3; // Used for reading DT signal
volatile boolean encChanged;
volatile long encPosition = 50;
volatile boolean up;
// ************************************************
// Interrupt service routine
// ************************************************
void isr() { // executed when a HIGH to LOW transition is detected on CLK
// volatile boolean CLK = digitalRead(PinCLK);
// You know that the Clock cycle is low because of attachInterrupt(0, isr, FALLING); // interrupt 0 is always connected to pin 2 on Arduino UNO
// The Clock pin could bounce if it is a mechanical switch so use a small capacitor to dampen the possibility of bounce in the circuit.
// The data pin hasn't changed for some time so if it is low the we are moving in one direction if it is high we are moving in the other
if (digitalRead(PinDT))
encPosition++;
else
encPosition--;
encPosition = min(100, max(0, encPosition));
encChanged = true;
setpoint = encPosition;
}
void setup() {
Serial.begin(9600);
lcd.begin(20, 4);
lcd.setCursor(0, 0); //Start at character 4 on line 0
// ************************************************
// Start up the DS18B20 One Wire Temperature Sensor
// ************************************************
sensors.begin();
if (!sensors.getAddress(tempSensor, 0))
{
lcd.setCursor(0, 1);
lcd.print(F("Sensor Error"));
}
// ************************************************
// PID
// ************************************************
windowStartTime = millis();
myPID.SetOutputLimits(0, WindowSize);
{
if(useSimulation)
{
for(byte i=0;i<50;i++)
{
theta[i]=outputStart;
}
modelTime = 0;
}
//Setup the pid
myPID.SetMode(AUTOMATIC);
if(tuning)
{
tuning=false;
// changeAutoTune();
tuning=true;
}
serialTime = 0;
// Initialize Relay Control:
pinMode(RelayPin, OUTPUT); // Output mode to drive relay
digitalWrite(RelayPin, LOW); // make sure it is off to start
// ************************************************
// Rotary Encoder
// ************************************************
pinMode(inPin, INPUT_PULLUP); // push button switch
pinMode(PinCLK, INPUT); // if you are not providing power you may need to use INPUT_PULLUP
pinMode(PinDT, INPUT); // if you are not providing power you may need to use INPUT_PULLUP
attachInterrupt(0, isr, FALLING); // interrupt 0 is always connected to pin 2 on Arduino UNO
}
}
void loop() {
int t = 100; //10 time a second... I struggled with sending fast to test too often to the LCD display and Serial ports so I suggest a no delay spam timer :)
// ************************************************
// Push Button
// ************************************************
reading = digitalRead(inPin);
if (reading == HIGH && previous == LOW && millis() - time > debounce) {
buttonPress();
}
previous = reading;
// ************************************************
// Rotary Encoder
// ************************************************
if (encChanged) { // do this only if rotation was detected
encChanged = false; // do NOT repeat IF loop until new rotation detected
static unsigned long SpamTimerSerial;
if ( (unsigned long)(millis() - SpamTimerSerial) >= (t)) {
SpamTimerSerial = millis();
Serial.print("Count = ");
Serial.println(encPosition);
}
}
// ************************************************
// LCD
// ************************************************
static unsigned long SpamTimerLCD;
if ( (unsigned long)(millis() - SpamTimerLCD) >= (t)) {
SpamTimerLCD = millis();
//lcd.setCursor(0, 0);
//lcd.print("Encoder: ");
//lcd.print(encPosition);
//lcd.setCursor(0, 1);
// lcd.print("Button State: ");
//lcd.print(state);
}
// ************************************************
// PID
// ************************************************
sensors.requestTemperatures();
unsigned long now = millis();
if(!useSimulation)
{ //pull the input in from the real world
input = ((sensors.getTempCByIndex(0))+offset);
}
if(tuning)
{
byte val = (aTune.Runtime());
if (val!=0)
{
tuning = false;
}
if(!tuning)
{ //we're done, set the tuning parameters
kp = aTune.GetKp();
ki = aTune.GetKi();
kd = aTune.GetKd();
myPID.SetTunings(kp,ki,kd);
AutoTuneHelper(false);
}
}
else myPID.Compute();
if(useSimulation)
{
theta[30]=output;
if(now>=modelTime)
{
modelTime +=100;
DoModel();
}
}
else
{
analogWrite(0,output);
}
/************************************************
* 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)
{
outputindicate = 1;
digitalWrite(RelayPin, HIGH);
}
else
{
digitalWrite(RelayPin, LOW) ;
outputindicate = 0;
}
//send-receive with processing if it's time
if(millis()>serialTime)
{
SerialReceive();
SerialSend();
serialTime+=500;
}
}
void changeAutoTune()
{
if(!tuning)
{
//Set the output to the desired starting frequency.
output=aTuneStartValue;
aTune.SetNoiseBand(aTuneNoise);
aTune.SetOutputStep(aTuneStep);
aTune.SetLookbackSec((int)aTuneLookBack);
AutoTuneHelper(true);
tuning = true;
}
else
{ //cancel autotune
aTune.Cancel();
tuning = false;
AutoTuneHelper(false);
}
}
void AutoTuneHelper(boolean start)
{
if(start)
ATuneModeRemember = myPID.GetMode();
else
myPID.SetMode(ATuneModeRemember);
}
void SerialSend()
{
int percent = ((output/255)*100);
lcd.clear();
lcd.setCursor(0,0); //Start at character 4 on line 0
lcd.print("Target: "); lcd.print(encPosition);lcd.print((char)223);
lcd.setCursor(0,1);
lcd.print("Current: "); lcd.print(input);lcd.print((char)223);
lcd.setCursor(0,2);
lcd.print("Output: "); lcd.print(outputindicate);
//lcd.setCursor(0,3);
//lcd.print(state);
Serial.print("setpoint: ");Serial.print(setpoint); Serial.print(" ");
Serial.print("input: ");Serial.print(input); Serial.print(" ");
Serial.print("output: ");Serial.print(output); Serial.print(" ");
if(tuning){
Serial.println("tuning mode");
} else {
Serial.print("kp: ");Serial.print(myPID.GetKp());Serial.print(" ");
Serial.print("ki: ");Serial.print(myPID.GetKi());Serial.print(" ");
Serial.print("kd: ");Serial.print(myPID.GetKd());Serial.println();
}
}
void SerialReceive()
{
if(Serial.available())
{
char b = Serial.read();
Serial.flush();
if((b=='1' && !tuning) || (b!='1' && tuning))changeAutoTune();
}
}
void DoModel()
{
//cycle the dead time
for(byte i=0;i<49;i++)
{
theta[i] = theta[i+1];
}
//compute the input
input = (kpmodel / taup) *(theta[0]-outputStart) + input*(1-1/taup) + ((float)random(-10,10))/100;
}
void buttonPress()
{
if (state == HIGH)
{
state = LOW;
Serial.println(state);
}
else
{
state = HIGH;
Serial.println(state);
}
time = millis();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment