@@ -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
Reference in New Issue
Block a user