Created
October 9, 2016 16:29
-
-
Save anonymous/8e4d15c4c99b0c1cd75bb11dae3825d2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ************************************************ | |
// 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