Revert "latest :)"

This reverts commit 288a1ccd09.
This commit is contained in:
2024-01-20 15:04:46 +01:00
parent 4b9e47433f
commit 8f2516faff
4218 changed files with 1345255 additions and 209 deletions
@@ -0,0 +1,86 @@
#include "AnalogButton_local.h"
//#include "WProgram.h"
#include "Arduino.h"
AnalogButton::AnalogButton(uint8_t analogPin, int buttonValueReturn,
int buttonValueUp, int buttonValueDown, int buttonValueOk)
{
// Store analog pin used to multiplex push button
buttonPin = analogPin;
// Add upper bound of tolerance for variation againts resistor values, temperature
// and other possible drift
buttonValueThresholdReturn = TOLERANCE*buttonValueReturn;
buttonValueThresholdUp = TOLERANCE*buttonValueUp;
buttonValueThresholdDown = TOLERANCE*buttonValueDown;
buttonValueThresholdOk = TOLERANCE*buttonValueOk;
}
button_t AnalogButton::read(void)
{
int buttonValue;
buttonValue = analogRead(buttonPin);
if (buttonValue >= BUTTON_NONE_THRESHOLD) return BUTTON_NONE;
if (buttonValue <= buttonValueThresholdReturn) return BUTTON_RETURN;
if (buttonValue <= buttonValueThresholdUp) return BUTTON_UP;
if (buttonValue <= buttonValueThresholdDown) return BUTTON_DOWN;
if (buttonValue <= buttonValueThresholdOk) return BUTTON_OK;
return BUTTON_NONE;
}
button_t AnalogButton::get(void)
{
static button_t buttonMask;
static buttonState_t buttonState;
static unsigned long debounceTimer;
button_t buttonValue;
button_t buttonStatus;
// Initialize button status
buttonStatus = BUTTON_NONE;
switch (buttonState)
{
case BUTTON_STATE_SCAN:
// Retrieve current button value
buttonValue = read();
// If button press is detected
if (buttonValue != BUTTON_NONE)
{
// Store current button press value
buttonMask = buttonValue;
// Retrieve current time
debounceTimer = millis();
debounceTimer += DEBOUNCE_PERIOD;
// Proceed to button debounce state
buttonState = BUTTON_STATE_DEBOUNCE;
}
break;
case BUTTON_STATE_DEBOUNCE:
if (read() == buttonMask)
{
// If debounce period is completed
if (millis() >= debounceTimer)
{
buttonStatus = buttonMask;
// Proceed to wait for the button to be released
buttonState = BUTTON_STATE_RELEASE;
}
}
break;
case BUTTON_STATE_RELEASE:
if (read() == BUTTON_NONE)
{
buttonMask = BUTTON_NONE;
buttonState = BUTTON_STATE_SCAN;
}
break;
}
return (buttonStatus);
}
@@ -0,0 +1,47 @@
#ifndef AnalogButton_h
#define AnalogButton_h
#include <inttypes.h>
enum button_t
{
BUTTON_NONE,
BUTTON_RETURN,
BUTTON_UP,
BUTTON_DOWN,
BUTTON_OK
};
enum buttonState_t
{
BUTTON_STATE_SCAN,
BUTTON_STATE_DEBOUNCE,
BUTTON_STATE_RELEASE
};
#define BUTTON_NONE_THRESHOLD 1000
#define TOLERANCE 1.1
#define DEBOUNCE_PERIOD 100
class AnalogButton
{
public:
AnalogButton(uint8_t analogPin, int buttonValueReturn,
int buttonValueUp, int buttonValueDown,
int buttonValueOk);
button_t get(void);
private:
button_t read(void);
// Analog pin used as button multiplexer
uint8_t buttonPin;
// Upper boound ADC value for each button
int buttonValueThresholdReturn;
int buttonValueThresholdUp;
int buttonValueThresholdDown;
int buttonValueThresholdOk;
};
#endif
@@ -0,0 +1,20 @@
#include <EEPROM.h>
#include <Arduino.h> // for type definitions
template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
const byte* p = (const byte*)(const void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.write(ee++, *p++);
return i;
}
template <class T> int EEPROM_readAnything(int ee, T& value)
{
byte* p = (byte*)(void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return i;
}
@@ -0,0 +1,230 @@
/*******************************************************************************
* MAX31855 Library
* Version: 1.10
* Date: 24-07-2012
* Company: Rocket Scream Electronics
* Website: www.rocketscream.com
*
* This is a MAX31855 library for Arduino. Please check our wiki
* (www.rocketscream.com/wiki) for more information on using this piece of
* library.
*
* This library is licensed under Creative Commons Attribution-ShareAlike 3.0
* Unported License.
*
* Revision Description
* ======== ===========
* 1.10 Added negative temperature support for both junction & thermocouple.
* 1.00 Initial public release.
*
*******************************************************************************/
#include "MAX31855_local.h"
MAX31855::MAX31855(unsigned char SO, unsigned char CS, unsigned char SCK)
{
so = SO;
cs = CS;
sck = SCK;
// MAX31855 data output pin
pinMode(so, INPUT);
// MAX31855 chip select input pin
pinMode(cs, OUTPUT);
// MAX31855 clock input pin
pinMode(sck, OUTPUT);
// Default output pins state
digitalWrite(cs, HIGH);
digitalWrite(sck, LOW);
}
/*******************************************************************************
* Name: readThermocouple
* Description: Read the thermocouple temperature either in Degree Celsius or
* Fahrenheit. Internally, the conversion takes place in the
* background within 100 ms. Values are updated only when the CS
* line is high.
*
* Argument Description
* ========= ===========
* 1. unit Unit of temperature required: CELSIUS or FAHRENHEIT
*
* Return Description
* ========= ===========
* temperature Temperature of the thermocouple either in Degree Celsius or
* Fahrenheit. If fault is detected, FAULT_OPEN, FAULT_SHORT_GND or
* FAULT_SHORT_VCC will be returned. These fault values are outside
* of the temperature range the MAX31855 is capable of.
*******************************************************************************/
double MAX31855::readThermocouple(unit_t unit)
{
unsigned long data;
double temperature;
// Initialize temperature
temperature = 0;
// Shift in 32-bit of data from MAX31855
data = readData();
// If fault is detected
if (data & 0x00010000)
{
// Check for fault type (3 LSB)
switch (data & 0x00000007)
{
// Open circuit
case 0x01:
temperature = FAULT_OPEN;
break;
// Thermocouple short to GND
case 0x02:
temperature = FAULT_SHORT_GND;
break;
// Thermocouple short to VCC
case 0x04:
temperature = FAULT_SHORT_VCC;
break;
}
}
// No fault detected
else
{
// Retrieve thermocouple temperature data and strip redundant data
data = data >> 18;
// Bit-14 is the sign
temperature = (data & 0x00001FFF);
// Check for negative temperature
if (data & 0x00002000)
{
// 2's complement operation
// Invert
data = ~data;
// Ensure operation involves lower 13-bit only
temperature = data & 0x00001FFF;
// Add 1 to obtain the positive number
temperature += 1;
// Make temperature negative
temperature *= -1;
}
// Convert to Degree Celsius
temperature *= 0.25;
// If temperature unit in Fahrenheit is desired
if (unit == FAHRENHEIT)
{
// Convert Degree Celsius to Fahrenheit
temperature = (temperature * 9.0 / 5.0) + 32;
}
}
return (temperature);
}
/*******************************************************************************
* Name: readJunction
* Description: Read the thermocouple temperature either in Degree Celsius or
* Fahrenheit. Internally, the conversion takes place in the
* background within 100 ms. Values are updated only when the CS
* line is high.
*
* Argument Description
* ========= ===========
* 1. unit Unit of temperature required: CELSIUS or FAHRENHEIT
*
* Return Description
* ========= ===========
* temperature Temperature of the cold junction either in Degree Celsius or
* Fahrenheit.
*
*******************************************************************************/
double MAX31855::readJunction(unit_t unit)
{
double temperature;
unsigned long data;
// Shift in 32-bit of data from MAX31855
data = readData();
// Strip fault data bits & reserved bit
data = data >> 4;
// Bit-12 is the sign
temperature = (data & 0x000007FF);
// Check for negative temperature
if (data & 0x00000800)
{
// 2's complement operation
// Invert
data = ~data;
// Ensure operation involves lower 11-bit only
temperature = data & 0x000007FF;
// Add 1 to obtain the positive number
temperature += 1;
// Make temperature negative
temperature *= -1;
}
// Convert to Degree Celsius
temperature *= 0.0625;
// If temperature unit in Fahrenheit is desired
if (unit == FAHRENHEIT)
{
// Convert Degree Celsius to Fahrenheit
temperature = (temperature * 9.0 / 5.0) + 32;
}
// Return the temperature
return (temperature);
}
/*******************************************************************************
* Name: readData
* Description: Shift in 32-bit of data from MAX31855 chip. Minimum clock pulse
* width is 100 ns. No delay is required in this case.
*
* Argument Description
* ========= ===========
* 1. NIL
*
* Return Description
* ========= ===========
* data 32-bit of data acquired from the MAX31855 chip.
*
*******************************************************************************/
unsigned long MAX31855::readData()
{
int bitCount;
unsigned long data;
// Clear data
data = 0;
// Select the MAX31855 chip
digitalWrite(cs, LOW);
// Shift in 32-bit of data
for (bitCount = 31; bitCount >= 0; bitCount--)
{
digitalWrite(sck, HIGH);
// If data bit is high
if (digitalRead(so))
{
// Need to type cast data type to unsigned long, else compiler will
// truncate to 16-bit
data |= ((unsigned long)1 << bitCount);
}
digitalWrite(sck, LOW);
}
// Deselect MAX31855 chip
digitalWrite(cs, HIGH);
return (data);
}
@@ -0,0 +1,35 @@
#ifndef MAX31855_H
#define MAX31855_H
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#define FAULT_OPEN 10000
#define FAULT_SHORT_GND 10001
#define FAULT_SHORT_VCC 10002
enum unit_t
{
CELSIUS,
FAHRENHEIT
};
class MAX31855
{
public:
MAX31855(unsigned char SO, unsigned char CS, unsigned char SCK);
double readThermocouple(unit_t unit);
double readJunction(unit_t unit);
private:
unsigned char so;
unsigned char cs;
unsigned char sck;
unsigned long readData();
};
#endif
@@ -0,0 +1,235 @@
/**********************************************************************************************
* Arduino PID AutoTune Library - Version 0.0.0
* by Brett Beauregard <br3ttb@gmail.com> brettbeauregard.com
*
* This Library is ported from the AutotunerPID Toolkit by William Spinelli
* (http://www.mathworks.com/matlabcentral/fileexchange/4652)
* Copyright (c) 2004
*
* This Library is licensed under the BSD License:
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
**********************************************************************************************/
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "PID_AutoTune_v0_local.h" //renamed to avoid conflict if Autotune library is installed on IDE
PID_ATune::PID_ATune(double *Input, double *Output)
{
input = Input;
output = Output;
controlType = 0; //default to PI
noiseBand = 0.5;
running = false;
oStep = 30;
SetLookbackSec(10);
lastTime = millis();
}
void PID_ATune::Cancel()
{
running = false;
}
int PID_ATune::Runtime()
{
justevaled = false;
if (peakCount > 9 && running)
{
running = false;
FinishUp();
return 1;
}
unsigned long now = millis();
if ((now - lastTime) < (unsigned long)sampleTime)
return false;
lastTime = now;
double refVal = *input;
justevaled = true;
if (!running)
{ //initialize working variables the first time around
peakType = 0;
peakCount = 0;
justchanged = false;
absMax = refVal;
absMin = refVal;
setpoint = refVal;
running = true;
initCount = 0;
outputStart = *output;
*output = outputStart + oStep;
}
else
{
if (refVal > absMax)
absMax = refVal;
if (refVal < absMin)
absMin = refVal;
}
//oscillate the output base on the input's relation to the setpoint
if (refVal > setpoint + noiseBand)
*output = outputStart - oStep;
else if (refVal < setpoint - noiseBand)
*output = outputStart + oStep;
//bool isMax=true, isMin=true;
isMax = true;
isMin = true;
//id peaks
for (int i = nLookBack - 1; i >= 0; i--)
{
double val = lastInputs[i];
if (isMax)
isMax = refVal > val;
if (isMin)
isMin = refVal < val;
lastInputs[i + 1] = lastInputs[i];
}
lastInputs[0] = refVal;
if (nLookBack < 9)
{ //we don't want to trust the maxes or mins until the inputs array has been filled
initCount++;
return 0;
}
if (isMax)
{
if (peakType == 0)
peakType = 1;
if (peakType == -1)
{
peakType = 1;
justchanged = true;
peak2 = peak1;
}
peak1 = now;
peaks[peakCount] = refVal;
}
else if (isMin)
{
if (peakType == 0)
peakType = -1;
if (peakType == 1)
{
peakType = -1;
peakCount++;
justchanged = true;
}
if (peakCount < 10)
peaks[peakCount] = refVal;
}
if (justchanged && peakCount > 2)
{ //we've transitioned. check if we can autotune based on the last peaks
double avgSeparation = (abs(peaks[peakCount - 1] - peaks[peakCount - 2]) + abs(peaks[peakCount - 2] - peaks[peakCount - 3])) / 2;
if (avgSeparation < 0.05 * (absMax - absMin))
{
FinishUp();
running = false;
return 1;
}
}
justchanged = false;
return 0;
}
void PID_ATune::FinishUp()
{
*output = outputStart;
//we can generate tuning parameters!
Ku = 4 * oStep / ((absMax - absMin) * 3.14159);
Pu = (double)(peak1 - peak2) / 1000;
}
double PID_ATune::GetKp()
{
return controlType == 1 ? 0.6 * Ku : 0.4 * Ku;
}
double PID_ATune::GetKi()
{
return controlType == 1 ? 1.2 * Ku / Pu : 0.48 * Ku / Pu; // Ki = Kc/Ti
}
double PID_ATune::GetKd()
{
return controlType == 1 ? 0.075 * Ku * Pu : 0; //Kd = Kc * Td
}
void PID_ATune::SetOutputStep(double Step)
{
oStep = Step;
}
double PID_ATune::GetOutputStep()
{
return oStep;
}
void PID_ATune::SetControlType(int Type) //0=PI, 1=PID
{
controlType = Type;
}
int PID_ATune::GetControlType()
{
return controlType;
}
void PID_ATune::SetNoiseBand(double Band)
{
noiseBand = Band;
}
double PID_ATune::GetNoiseBand()
{
return noiseBand;
}
void PID_ATune::SetLookbackSec(int value)
{
if (value < 1)
value = 1;
if (value < 25)
{
nLookBack = value * 4;
sampleTime = 250;
}
else
{
nLookBack = 100;
sampleTime = value * 10;
}
}
int PID_ATune::GetLookbackSec()
{
return nLookBack * sampleTime / 1000;
}
@@ -0,0 +1,53 @@
#ifndef PID_AutoTune_v0
#define PID_AutoTune_v0
#define LIBRARY_VERSION 0.0.0
class PID_ATune
{
public:
//commonly used functions **************************************************************************
PID_ATune(double *, double *); // * Constructor. links the Autotune to a given PID
int Runtime(); // * Similar to the PID Compue function, returns non 0 when done
void Cancel(); // * Stops the AutoTune
void SetOutputStep(double); // * how far above and below the starting value will the output step?
double GetOutputStep(); //
void SetControlType(int); // * Determies if the tuning parameters returned will be PI (D=0)
int GetControlType(); // or PID. (0=PI, 1=PID)
void SetLookbackSec(int); // * how far back are we looking to identify peaks
int GetLookbackSec(); //
void SetNoiseBand(double); // * the autotune will ignore signal chatter smaller than this value
double GetNoiseBand(); // this should be acurately set
double GetKp(); // * once autotune is complete, these functions contain the
double GetKi(); // computed tuning parameters.
double GetKd(); //
private:
void FinishUp();
bool isMax, isMin;
double *input, *output;
double setpoint;
double noiseBand;
int controlType;
bool running;
unsigned long peak1, peak2, lastTime;
int sampleTime;
int nLookBack;
int peakType;
double lastInputs[100];
double peaks[10];
int peakCount;
bool justchanged;
bool justevaled;
int initCount;
double absMax, absMin;
double oStep;
double outputStart;
double Ku, Pu;
};
#endif
@@ -0,0 +1,192 @@
/**********************************************************************************************
* Arduino PID Library - Version 1.0.1
* by Brett Beauregard <br3ttb@gmail.com> brettbeauregard.com
*
* This Library is licensed under a GPLv3 License
**********************************************************************************************/
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "PID_v1_local.h" //renamed to avoid conflict if PID library is installed on IDE
/*Constructor (...)*********************************************************
* The parameters specified here are those for for which we can't set up
* reliable defaults, so we need to have the user set them.
***************************************************************************/
PID::PID(double* Input, double* Output, double* Setpoint,
double Kp, double Ki, double Kd, int ControllerDirection)
{
PID::SetOutputLimits(0, 255); //default output limit corresponds to
//the arduino pwm limits
SampleTime = 100; //default Controller Sample Time is 0.1 seconds
PID::SetControllerDirection(ControllerDirection);
PID::SetTunings(Kp, Ki, Kd);
lastTime = millis()-SampleTime;
inAuto = false;
myOutput = Output;
myInput = Input;
mySetpoint = Setpoint;
}
/* Compute() **********************************************************************
* This, as they say, is where the magic happens. this function should be called
* every time "void loop()" executes. the function will decide for itself whether a new
* pid Output needs to be computed
**********************************************************************************/
void PID::Compute()
{
if(!inAuto) return;
unsigned long now = millis();
unsigned long timeChange = (now - lastTime);
if(timeChange>=(unsigned long)SampleTime)
{
/*Compute all the working error variables*/
double input = *myInput;
double error = *mySetpoint - input;
ITerm+= (ki * error);
if(ITerm > outMax) ITerm= outMax;
else if(ITerm < outMin) ITerm= outMin;
double dInput = (input - lastInput);
/*Compute PID Output*/
double output = kp * error + ITerm- kd * dInput;
if(output > outMax) output = outMax;
else if(output < outMin) output = outMin;
*myOutput = output;
/*Remember some variables for next time*/
lastInput = input;
lastTime = now;
}
}
/* SetTunings(...)*************************************************************
* This function allows the controller's dynamic performance to be adjusted.
* it's called automatically from the constructor, but tunings can also
* be adjusted on the fly during normal operation
******************************************************************************/
void PID::SetTunings(double Kp, double Ki, double Kd)
{
if (Kp<0 || Ki<0 || Kd<0) return;
dispKp = Kp; dispKi = Ki; dispKd = Kd;
double SampleTimeInSec = ((double)SampleTime)/1000;
kp = Kp;
ki = Ki * SampleTimeInSec;
kd = Kd / SampleTimeInSec;
if(controllerDirection ==REVERSE)
{
kp = (0 - kp);
ki = (0 - ki);
kd = (0 - kd);
}
}
/* SetSampleTime(...) *********************************************************
* sets the period, in Milliseconds, at which the calculation is performed
******************************************************************************/
void PID::SetSampleTime(int NewSampleTime)
{
if (NewSampleTime > 0)
{
double ratio = (double)NewSampleTime
/ (double)SampleTime;
ki *= ratio;
kd /= ratio;
SampleTime = (unsigned long)NewSampleTime;
}
}
/* SetOutputLimits(...)****************************************************
* This function will be used far more often than SetInputLimits. while
* the input to the controller will generally be in the 0-1023 range (which is
* the default already,) the output will be a little different. maybe they'll
* be doing a time window and will need 0-8000 or something. or maybe they'll
* want to clamp it from 0-125. who knows. at any rate, that can all be done
* here.
**************************************************************************/
void PID::SetOutputLimits(double Min, double Max)
{
if(Min >= Max) return;
outMin = Min;
outMax = Max;
if(inAuto)
{
if(*myOutput > outMax) *myOutput = outMax;
else if(*myOutput < outMin) *myOutput = outMin;
if(ITerm > outMax) ITerm= outMax;
else if(ITerm < outMin) ITerm= outMin;
}
}
/* SetMode(...)****************************************************************
* Allows the controller Mode to be set to manual (0) or Automatic (non-zero)
* when the transition from manual to auto occurs, the controller is
* automatically initialized
******************************************************************************/
void PID::SetMode(int Mode)
{
bool newAuto = (Mode == AUTOMATIC);
if(newAuto == !inAuto)
{ /*we just went from manual to auto*/
PID::Initialize();
}
inAuto = newAuto;
}
/* Initialize()****************************************************************
* does all the things that need to happen to ensure a bumpless transfer
* from manual to automatic mode.
******************************************************************************/
void PID::Initialize()
{
ITerm = *myOutput;
lastInput = *myInput;
if(ITerm > outMax) ITerm = outMax;
else if(ITerm < outMin) ITerm = outMin;
}
/* SetControllerDirection(...)*************************************************
* The PID will either be connected to a DIRECT acting process (+Output leads
* to +Input) or a REVERSE acting process(+Output leads to -Input.) we need to
* know which one, because otherwise we may increase the output when we should
* be decreasing. This is called from the constructor.
******************************************************************************/
void PID::SetControllerDirection(int Direction)
{
if(inAuto && Direction !=controllerDirection)
{
kp = (0 - kp);
ki = (0 - ki);
kd = (0 - kd);
}
controllerDirection = Direction;
}
/* Status Funcions*************************************************************
* Just because you set the Kp=-1 doesn't mean it actually happened. these
* functions query the internal state of the PID. they're here for display
* purposes. this are the functions the PID Front-end uses for example
******************************************************************************/
double PID::GetKp(){ return dispKp; }
double PID::GetKi(){ return dispKi;}
double PID::GetKd(){ return dispKd;}
int PID::GetMode(){ return inAuto ? AUTOMATIC : MANUAL;}
int PID::GetDirection(){ return controllerDirection;}
@@ -0,0 +1,81 @@
#ifndef PID_v1_h
#define PID_v1_h
#define LIBRARY_VERSION 1.0.0
class PID
{
public:
//Constants used in some of the functions below
#define AUTOMATIC 1
#define MANUAL 0
#define DIRECT 0
#define REVERSE 1
//commonly used functions **************************************************************************
PID(double*, double*, double*, // * constructor. links the PID to the Input, Output, and
double, double, double, int); // Setpoint. Initial tuning parameters are also set here
void SetMode(int Mode); // * sets PID to either Manual (0) or Auto (non-0)
void Compute(); // * performs the PID calculation. it should be
// called every time loop() cycles. ON/OFF and
// calculation frequency can be set using SetMode
// SetSampleTime respectively
void SetOutputLimits(double, double); //clamps the output to a specific range. 0-255 by default, but
//it's likely the user will want to change this depending on
//the application
//available but not commonly used functions ********************************************************
void SetTunings(double, double, // * While most users will set the tunings once in the
double); // constructor, this function gives the user the option
// of changing tunings during runtime for Adaptive control
void SetControllerDirection(int); // * Sets the Direction, or "Action" of the controller. DIRECT
// means the output will increase when error is positive. REVERSE
// means the opposite. it's very unlikely that this will be needed
// once it is set in the constructor.
void SetSampleTime(int); // * sets the frequency, in Milliseconds, with which
// the PID calculation is performed. default is 100
//Display functions ****************************************************************
double GetKp(); // These functions query the pid for interal values.
double GetKi(); // they were created mainly for the pid front-end,
double GetKd(); // where it's important to know what is actually
int GetMode(); // inside the PID.
int GetDirection(); //
private:
void Initialize();
double dispKp; // * we'll hold on to the tuning parameters in user-entered
double dispKi; // format for display purposes
double dispKd; //
double kp; // * (P)roportional Tuning Parameter
double ki; // * (I)ntegral Tuning Parameter
double kd; // * (D)erivative Tuning Parameter
int controllerDirection;
double *myInput; // * Pointers to the Input, Output, and Setpoint variables
double *myOutput; // This creates a hard link between the variables and the
double *mySetpoint; // PID, freeing the user from having to constantly tell us
// what these values are. with pointers we'll just know.
unsigned long lastTime;
double ITerm, lastInput;
int SampleTime;
double outMin, outMax;
bool inAuto;
};
#endif
@@ -0,0 +1,33 @@
/********************************************************
* osPID Firmware, Version 1.7
* by Brett Beauregard & Rocket Scream
* License: GPLv3 & BSD License (For autotune)
* 27 April 2016
********************************************************/
NOTE: THIS FIRMWARE IS CONFIGURED FOR DIGITAL OUTPUT CARD
V1.5 & TEMPERATURE INPUT CARD V1.2. IF YOU ARE USING
A DIFFERENT I/O CONFIGURATION BE SURE TO UN-COMMENT THE
APPROPRIATE #DEFINE STATEMENTS IN IO.H.
Updates for version 1.7
-output is disabled if input is in error state for both thermistor and thermocouple
Updates for version 1.6
-added support for v1.5 of the Temperature Input card (MAX31855 Thermocouple chip)
Updates for version 1.5
-restructured code to allow for different IO cards
-added reflow profile support
-eliminated LCD flicker
-error message when thermocouple is disconnected
-extreme code size / RAM improvement (mainly menu and EEPRom)
-consolodated the code into fewer files
* osPID_Firmware.ino - Just about everything
* io.h - IO card code. pre-compiler flags control which card code is used
* EEPROMAnything.h - halley's amazing EEPROMWriteAnything code.
* AnalogButton .cpp _local.h - ospid button-reading/debounce code
* PID_AutoTune_v0 .cpp _local.h - local copy of the autotune library (to avoid
conflicts with possibly pre-installed copies)
* PID_v1 .ccp _local.h - local copy of the PID library
* max6675 .cpp _local.h - local copy of the max6675 library, used by the input card.
@@ -0,0 +1,530 @@
/*******************************************************************************
* The osPID Kit comes with swappable IO cards which are supported by different
* device drivers & libraries. For the osPID firmware to correctly communicate with
* your configuration, you must uncomment the appropriate "define" statements below.
* Please take note that only 1 input card and 1 output card can be used at a time.
* List of available IO cards:
*
* Input Cards
* ===========
* 1. TEMP_INPUT_V110:
* Temperature Basic V1.10 with 1 thermistor & 1 type-K thermocouple (MAX6675)
* interface.
* 2. TEMP_INPUT_V120:
* Temperature Basic V1.20 with 1 thermistor & 1 type-K thermocouple
* (MAX31855KASA) interface.
* 3. PROTOTYPE_INPUT:
* Generic prototype card with input specified by user. Please add necessary
* input processing in the section below.
*
* Output Cards
* ============
* 1. DIGITAL_OUTPUT_V120:
* Output card with 1 SSR & 2 relay output.
* 2. DIGITAL_OUTPUT_V150:
* Output card with 1 SSR & 2 relay output. Similar to V1.20 except LED mount
* orientation.
* 3. PROTOTYPE_OUTPUT:
* Generic prototype card with output specified by user. Please add necessary
* output processing in the section below.
*
* This file is licensed under Creative Commons Attribution-ShareAlike 3.0
* Unported License.
*
*******************************************************************************/
// ***** INPUT CARD *****
//#define TEMP_INPUT_V110
#define TEMP_INPUT_V120
//#define PROTOTYPE_INPUT
// ***** OUTPUT CARD *****
//#define DIGITAL_OUTPUT_V120
#define DIGITAL_OUTPUT_V150
//#define PROTOTYPE_OUTPUT
union { // This Data structure lets
byte asBytes[32]; // us take the byte array
float asFloat[8]; // sent from processing and
} // easily convert it to a
serialXfer; // float array
byte b1,b2;
#ifdef TEMP_INPUT_V110
#include "max6675_local.h"
const byte thermistorPin = A6;
const byte thermocoupleCS = 10;
const byte thermocoupleSO = 12;
const byte thermocoupleCLK = 13;
byte inputType = 0;
double THERMISTORNOMINAL = 10;
double BCOEFFICIENT = 1;
double TEMPERATURENOMINAL = 293.15;
double REFERENCE_RESISTANCE = 10;
MAX6675 thermocouple(thermocoupleCLK, thermocoupleCS, thermocoupleSO);
// EEPROM backup
void EEPROMBackupInputParams(int offset)
{
EEPROM.write(offset, inputType);
EEPROM_writeAnything(offset+2,THERMISTORNOMINAL);
EEPROM_writeAnything(offset+6,BCOEFFICIENT);
EEPROM_writeAnything(offset+10,TEMPERATURENOMINAL);
EEPROM_writeAnything(offset+14,REFERENCE_RESISTANCE);
}
// EEPROM restore
void EEPROMRestoreInputParams(int offset)
{
inputType = EEPROM.read(offset);
EEPROM_readAnything(offset+2,THERMISTORNOMINAL);
EEPROM_readAnything(offset+6,BCOEFFICIENT);
EEPROM_readAnything(offset+10,TEMPERATURENOMINAL);
EEPROM_readAnything(offset+14,REFERENCE_RESISTANCE);
}
void InitializeInputCard()
{
}
void InputSerialReceiveStart()
{
}
void InputSerialReceiveDuring(byte val, byte index)
{
if(index==1) b1 = val;
else if(index<18) serialXfer.asBytes[index-2] = val;
}
void InputSerialReceiveAfter(int eepromOffset)
{
inputType = b1;
THERMISTORNOMINAL = serialXfer.asFloat[0];
BCOEFFICIENT = serialXfer.asFloat[1];
TEMPERATURENOMINAL = serialXfer.asFloat[2];
REFERENCE_RESISTANCE = serialXfer.asFloat[3];
EEPROMBackupInputParams(eepromOffset);
}
void InputSerialSend()
{
Serial.print((int)inputType);
Serial.print(" ");
Serial.print(THERMISTORNOMINAL);
Serial.print(" ");
Serial.print(BCOEFFICIENT);
Serial.print(" ");
Serial.print(TEMPERATURENOMINAL);
Serial.print(" ");
Serial.println(REFERENCE_RESISTANCE);
}
void InputSerialID()
{
Serial.print(" IID1");
}
double readThermistorTemp(int voltage)
{
float R = REFERENCE_RESISTANCE / (1024.0/(float)voltage - 1);
float steinhart;
steinhart = R / THERMISTORNOMINAL; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro)
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
steinhart = 1.0 / steinhart; // Invert
steinhart -= 273.15; // convert to C
return steinhart;
}
double ReadInputFromCard()
{
if(inputType == 0) return thermocouple.readCelsius();
else if(inputType == 1)
{
int adcReading = analogRead(thermistorPin);
if ((adcReading == 0) || (adcReading == 1023))
{
return NAN;
}
else
{
return readThermistorTemp(adcReading);
}
}
}
#endif /*TEMP_INPUT_V110*/
#ifdef TEMP_INPUT_V120
#include "MAX31855_local.h"
const byte thermistorPin = A6;
const byte thermocoupleCS = 10;
const byte thermocoupleSO = 12;
const byte thermocoupleCLK = 13;
byte inputType = 0;
double THERMISTORNOMINAL = 10;
double BCOEFFICIENT = 1;
double TEMPERATURENOMINAL = 293.15;
double REFERENCE_RESISTANCE = 10;
MAX31855 thermocouple(thermocoupleSO, thermocoupleCS, thermocoupleCLK);
// EEPROM backup
void EEPROMBackupInputParams(int offset)
{
EEPROM.write(offset, inputType);
EEPROM_writeAnything(offset+2,THERMISTORNOMINAL);
EEPROM_writeAnything(offset+6,BCOEFFICIENT);
EEPROM_writeAnything(offset+10,TEMPERATURENOMINAL);
EEPROM_writeAnything(offset+14,REFERENCE_RESISTANCE);
}
// EEPROM restore
void EEPROMRestoreInputParams(int offset)
{
inputType = EEPROM.read(offset);
EEPROM_readAnything(offset+2,THERMISTORNOMINAL);
EEPROM_readAnything(offset+6,BCOEFFICIENT);
EEPROM_readAnything(offset+10,TEMPERATURENOMINAL);
EEPROM_readAnything(offset+14,REFERENCE_RESISTANCE);
}
void InitializeInputCard()
{
}
void InputSerialReceiveStart()
{
}
void InputSerialReceiveDuring(byte val, byte index)
{
if(index==1) b1 = val;
else if(index<18) serialXfer.asBytes[index-2] = val;
}
void InputSerialReceiveAfter(int eepromOffset)
{
inputType = b1;
THERMISTORNOMINAL = serialXfer.asFloat[0];
BCOEFFICIENT = serialXfer.asFloat[1];
TEMPERATURENOMINAL = serialXfer.asFloat[2];
REFERENCE_RESISTANCE = serialXfer.asFloat[3];
EEPROMBackupInputParams(eepromOffset);
}
void InputSerialSend()
{
Serial.print((int)inputType);
Serial.print(" ");
Serial.print(THERMISTORNOMINAL);
Serial.print(" ");
Serial.print(BCOEFFICIENT);
Serial.print(" ");
Serial.print(TEMPERATURENOMINAL);
Serial.print(" ");
Serial.println(REFERENCE_RESISTANCE);
}
void InputSerialID()
{
Serial.print(" IID2");
}
double readThermistorTemp(int voltage)
{
float R = REFERENCE_RESISTANCE / (1024.0/(float)voltage - 1);
float steinhart;
steinhart = R / THERMISTORNOMINAL; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro)
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
steinhart = 1.0 / steinhart; // Invert
steinhart -= 273.15; // convert to C
return steinhart;
}
double ReadInputFromCard()
{
if(inputType == 0)
{
double val = thermocouple.readThermocouple(CELSIUS);
if (val==FAULT_OPEN|| val==FAULT_SHORT_GND|| val==FAULT_SHORT_VCC)val = NAN;
return val;
}
else if(inputType == 1)
{
int adcReading = analogRead(thermistorPin);
// If either thermistor or reference resistor is not connected
if ((adcReading == 0) || (adcReading == 1023))
{
return NAN;
}
else
{
return readThermistorTemp(adcReading);
}
}
}
#endif /*TEMP_INPUT_V120*/
#ifdef PROTOTYPE_INPUT
/*Include any libraries and/or global variables here*/
float flt1_i=0, flt2_i=0, flt3_i=0, flt4_i=0;
byte bt1_i=0, bt2_i=0, bt3_i=0, bt4_i=0;
void EEPROMBackupInputParams(int offset)
{
EEPROM_writeAnything(offset, bt1_i);
EEPROM_writeAnything(offset+1, bt2_i);
EEPROM_writeAnything(offset+2, bt3_i);
EEPROM_writeAnything(offset+3, bt4_i);
EEPROM_writeAnything(offset+4,flt1_i);
EEPROM_writeAnything(offset+8,flt2_i);
EEPROM_writeAnything(offset+12,flt3_i);
EEPROM_writeAnything(offset+16,flt4_i);
}
void EEPROMRestoreInputParams(int offset)
{
EEPROM_readAnything(offset, bt1_i);
EEPROM_readAnything(offset+1, bt2_i);
EEPROM_readAnything(offset+2, bt3_i);
EEPROM_readAnything(offset+3, bt4_i);
EEPROM_readAnything(offset+4,flt1_i);
EEPROM_readAnything(offset+8,flt2_i);
EEPROM_readAnything(offset+12,flt3_i);
EEPROM_readAnything(offset+16,flt4_i);
}
void InitializeInputCard()
{
}
void InputSerialReceiveStart()
{
}
void InputSerialReceiveDuring(byte val, byte index)
{
if(index==1) bt1_i = val;
else if(index==2) bt2_i = val;
else if(index==3) bt3_i = val;
else if(index==4) bt4_i = val;
else if(index<22) serialXfer.asBytes[index-5] = val;
}
void InputSerialReceiveAfter(int eepromOffset)
{
flt1_i = serialXfer.asFloat[0];
flt2_i = serialXfer.asFloat[1];
flt3_i = serialXfer.asFloat[2];
flt4_i = serialXfer.asFloat[3];
EEPROMBackupInputParams(eepromOffset);
}
void InputSerialSend()
{
Serial.print(int(bt1_i));
Serial.print(" ");
Serial.print(int(bt2_i));
Serial.print(" ");
Serial.print(int(bt3_i));
Serial.print(" ");
Serial.print(int(bt4_i));
Serial.print(" ");
Serial.print(flt1_i);
Serial.print(" ");
Serial.print(flt2_i);
Serial.print(" ");
Serial.print(flt3_i);
Serial.print(" ");
Serial.println(flt4_i);
}
void InputSerialID()
{
Serial.print(" IID0");
}
double ReadInputFromCard()
{
/*your code here*/
return 0;
}
#endif /*PROTOTYPE_INPUT*/
#if defined(DIGITAL_OUTPUT_V120) || defined(DIGITAL_OUTPUT_V150)
byte outputType = 1;
const byte RelayPin = 5;
const byte SSRPin = 6;
//unsigned long windowStartTime;
double outWindowSec = 5.0;
unsigned long WindowSize = 5000;
void setOutputWindow(double val)
{
unsigned long temp = (unsigned long)(val*1000);
if(temp<500)temp = 500;
outWindowSec = (double)temp/1000;
if(temp!=WindowSize)
{
WindowSize = temp;
}
}
void EEPROMBackupOutputParams(int offset)
{
EEPROM.write(offset, outputType);
EEPROM_writeAnything(offset+1, WindowSize);
}
void EEPROMRestoreOutputParams(int offset)
{
outputType = EEPROM.read(offset);
EEPROM_readAnything(offset+1, WindowSize);
}
void InitializeOutputCard()
{
pinMode(RelayPin, OUTPUT);
pinMode(SSRPin, OUTPUT);
}
void OutputSerialReceiveStart()
{
}
void OutputSerialReceiveDuring(byte val, byte index)
{
if(index==1) b1 = val;
else if(index<6) serialXfer.asBytes[index-2] = val;
}
void OutputSerialReceiveAfter(int eepromOffset)
{
if(outputType != b1)
{
if (b1==0)digitalWrite(SSRPin, LOW);
else if(b1==1) digitalWrite( RelayPin,LOW); //turn off the other pin
outputType=b1;
}
outWindowSec = serialXfer.asFloat[0];
setOutputWindow(outWindowSec);
EEPROMBackupOutputParams(eepromOffset);
}
void OutputSerialID()
{
Serial.print(" OID1");
}
void WriteToOutputCard(double value)
{
unsigned long wind = millis() % WindowSize; // (millis() - windowStartTime);
/*if(wind>WindowSize)
{
wind -= WindowSize;
windowStartTime += WindowSize;
}*/
unsigned long oVal = (unsigned long)(value*(double)WindowSize/ 100.0);
if(outputType == 0) digitalWrite(RelayPin ,(oVal>wind) ? HIGH : LOW);
else if(outputType == 1) digitalWrite(SSRPin ,(oVal>wind) ? HIGH : LOW);
}
// Serial send & receive
void OutputSerialSend()
{
Serial.print((int)outputType);
Serial.print(" ");
Serial.println(outWindowSec);
}
#endif /*DIGITAL_OUTPUT_V120 & DIGITAL_OUTPUT_V150*/
#ifdef PROTOTYPE_OUTPUT
float flt1_o=0, flt2_o=0, flt3_o=0, flt4_o=0;
byte bt1_o=0, bt2_o=0, bt3_o=0, bt4_o=0;
void EEPROMBackupOutputParams(int offset)
{
EEPROM_writeAnything(offset, bt1_o);
EEPROM_writeAnything(offset+1, bt2_o);
EEPROM_writeAnything(offset+2, bt3_o);
EEPROM_writeAnything(offset+3, bt4_o);
EEPROM_writeAnything(offset+4,flt1_o);
EEPROM_writeAnything(offset+8,flt2_o);
EEPROM_writeAnything(offset+12,flt3_o);
EEPROM_writeAnything(offset+16,flt4_o);
}
void EEPROMRestoreOutputParams(int offset)
{
EEPROM_readAnything(offset, bt1_o);
EEPROM_readAnything(offset+1, bt2_o);
EEPROM_readAnything(offset+2, bt3_o);
EEPROM_readAnything(offset+3, bt4_o);
EEPROM_readAnything(offset+4,flt1_o);
EEPROM_readAnything(offset+8,flt2_o);
EEPROM_readAnything(offset+12,flt3_o);
EEPROM_readAnything(offset+16,flt4_o);
}
void InitializeOutputCard()
{
}
void OutputSerialReceiveStart()
{
}
void OutputSerialReceiveDuring(byte val, byte index)
{
if(index==1) bt1_o = val;
else if(index==2) bt2_o = val;
else if(index==3) bt3_o = val;
else if(index==4) bt4_o = val;
else if(index<22) serialXfer.asBytes[index-5] = val;
}
void OutputSerialReceiveAfter(int eepromOffset)
{
flt1_o = serialXfer.asFloat[0];
flt2_o = serialXfer.asFloat[1];
flt3_o = serialXfer.asFloat[2];
flt4_o = serialXfer.asFloat[3];
EEPROMBackupOutputParams(eepromOffset);
}
void OutputSerialID()
{
Serial.print(" OID0");
}
void WriteToOutputCard(double value)
{
}
// Serial send & receive
void OutputSerialSend()
{
Serial.print(int(bt1_o));
Serial.print(" ");
Serial.print(int(bt2_o));
Serial.print(" ");
Serial.print(int(bt3_o));
Serial.print(" ");
Serial.print(int(bt4_o));
Serial.print(" ");
Serial.print(flt1_o);
Serial.print(" ");
Serial.print(flt2_o);
Serial.print(" ");
Serial.print(flt3_o);
Serial.print(" ");
Serial.println(flt4_o);
}
#endif /*PROTOTYPE_OUTPUT*/
@@ -0,0 +1,73 @@
// this library is public domain. enjoy!
// www.ladyada.net/learn/sensors/thermocouple
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>
#include "max6675_local.h"
MAX6675::MAX6675(int8_t SCLK, int8_t CS, int8_t MISO)
{
sclk = SCLK;
cs = CS;
miso = MISO;
//define pin modes
pinMode(cs, OUTPUT);
pinMode(sclk, OUTPUT);
pinMode(miso, INPUT);
digitalWrite(cs, HIGH);
}
double MAX6675::readCelsius(void)
{
uint16_t v;
digitalWrite(cs, LOW);
_delay_ms(1);
v = spiread();
v <<= 8;
v |= spiread();
digitalWrite(cs, HIGH);
if (v & 0x4)
{
// uh oh, no thermocouple attached!
return NAN;
//return -100;
}
v >>= 3;
return v * 0.25;
}
double MAX6675::readFarenheit(void)
{
return readCelsius() * 9.0 / 5.0 + 32;
}
byte MAX6675::spiread(void)
{
int i;
byte d = 0;
for (i = 7; i >= 0; i--)
{
digitalWrite(sclk, LOW);
_delay_ms(1);
if (digitalRead(miso))
{
//set the bit to 0 no matter what
d |= (1 << i);
}
digitalWrite(sclk, HIGH);
_delay_ms(1);
}
return d;
}
@@ -0,0 +1,21 @@
// this library is public domain. enjoy!
// www.ladyada.net/learn/sensors/thermocouple
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
class MAX6675
{
public:
MAX6675(int8_t SCLK, int8_t CS, int8_t MISO);
double readCelsius(void);
double readFarenheit(void);
private:
int8_t sclk, miso, cs;
uint8_t spiread(void);
};
File diff suppressed because it is too large Load Diff