updated control loop + draw target graph +temps
This commit is contained in:
@@ -88,10 +88,8 @@
|
||||
#define SMOOTH_FONT
|
||||
|
||||
|
||||
// Nucleo-F767ZI has a ~216MHZ CPU clock, this is divided by 4, 8, 16 etc
|
||||
|
||||
//#define SPI_FREQUENCY 27000000 // 27MHz SPI clock
|
||||
#define SPI_FREQUENCY 55000000 // 55MHz is over-clocking ILI9341 but seems to work reliably!
|
||||
#define SPI_FREQUENCY 27000000 // 27MHz SPI clock
|
||||
//#define SPI_FREQUENCY 55000000 // 55MHz is over-clocking ILI9341 but seems to work reliably!
|
||||
|
||||
#define SPI_READ_FREQUENCY 15000000 // Reads need a slower SPI clock, probably ends up at 13.75MHz (CPU clock/16)
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@ monitor_speed = 115200
|
||||
lib_deps =
|
||||
yuriisalimov/MAX6675_Thermocouple@^2.0.2
|
||||
bodmer/TFT_eSPI@^2.3.70
|
||||
br3ttb/PID@^1.2.1
|
||||
;siruli/MAX6675@^2.1.0
|
||||
lib_ldf_mode = deep+
|
||||
build_flags =
|
||||
-D USER_SETUP_LOADED=1
|
||||
-include include/ILI9341_STM32.h
|
||||
-D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC
|
||||
-D USBCON
|
||||
-D PIO_FRAMEWORK_ARDUINO_USB_FULLSPEED_FULLMODE
|
||||
|
||||
|
||||
@@ -12,3 +12,11 @@
|
||||
#define THERM_CL PB6
|
||||
|
||||
#define HEAT_OUT PA2
|
||||
|
||||
#define BUZZER A4
|
||||
|
||||
#define BUTTON_SELECT 0
|
||||
#define BUTTON_AXIS_Y 0
|
||||
#define BUTTON_AXIS_X 0
|
||||
#define BUTTON_MENU 0
|
||||
#define BUTTON_BACK 0
|
||||
|
||||
@@ -1,10 +1,281 @@
|
||||
#include "button.h"
|
||||
|
||||
#include "controlloop.h"
|
||||
|
||||
#define DEBOUNCE_MS 100
|
||||
Button start_btn(0, false, DEBOUNCE_MS, true);
|
||||
Button stop_btn(0, false, DEBOUNCE_MS, true);
|
||||
Button set_btn(0, false, DEBOUNCE_MS, true);
|
||||
|
||||
Button *buttons[] = {&start_btn, &stop_btn, &set_btn};
|
||||
|
||||
void setButton(uint8_t index, bool state)
|
||||
{
|
||||
buttons[index]->setButton(state);
|
||||
}
|
||||
|
||||
void readAllButtons(void)
|
||||
{
|
||||
for (auto &&button : buttons)
|
||||
{
|
||||
button->read();
|
||||
}
|
||||
}
|
||||
|
||||
void initButton(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void handleButton(void)
|
||||
{
|
||||
readAllButtons();
|
||||
|
||||
if (start_btn.isPressed())
|
||||
{
|
||||
setReflowStatus(true);
|
||||
}
|
||||
if (stop_btn.isPressed())
|
||||
{
|
||||
setReflowStatus(false);
|
||||
}
|
||||
if (set_btn.isPressed())
|
||||
{
|
||||
//enter menu
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*
|
||||
* Arduino Button Library v1.0 *
|
||||
* Jack Christensen May 2011, published Mar 2012 *
|
||||
* *
|
||||
* Library for reading momentary contact switches like tactile button *
|
||||
* switches. Intended for use in state machine constructs. *
|
||||
* Use the read() function to read all buttons in the main loop, *
|
||||
* which should execute as fast as possible. *
|
||||
* *
|
||||
* This work is licensed under the Creative Commons Attribution- *
|
||||
* ShareAlike 3.0 Unported License. To view a copy of this license, *
|
||||
* visit http://creativecommons.org/licenses/by-sa/3.0/ or send a *
|
||||
* letter to Creative Commons, 171 Second Street, Suite 300, *
|
||||
* San Francisco, California, 94105, USA. *
|
||||
*----------------------------------------------------------------------*/
|
||||
|
||||
#include "Button.h"
|
||||
|
||||
/*----------------------------------------------------------------------*
|
||||
* Button(pin, puEnable, invert, dbTime) instantiates a button object. *
|
||||
* pin Is the Arduino pin the button is connected to. *
|
||||
* puEnable Enables the AVR internal pullup resistor if != 0 (can also *
|
||||
* use true or false). *
|
||||
* invert If invert == 0, interprets a high state as pressed, low as *
|
||||
* released. If invert != 0, interprets a high state as *
|
||||
* released, low as pressed (can also use true or false). *
|
||||
* dbTime Is the debounce time in milliseconds. *
|
||||
* *
|
||||
* (Note that invert cannot be implied from puEnable since an external *
|
||||
* pullup could be used.) *
|
||||
*----------------------------------------------------------------------*/
|
||||
Button::Button(uint8_t pin, uint8_t invert, uint32_t dbTime, bool isTouch) : _isTouchButton(isTouch)
|
||||
{
|
||||
if (!_isTouchButton)
|
||||
{
|
||||
_pin = pin;
|
||||
pinMode(_pin, INPUT_PULLUP);
|
||||
_state = digitalRead(_pin);
|
||||
}
|
||||
_invert = invert;
|
||||
_dbTime = dbTime;
|
||||
if (_invert != 0)
|
||||
_state = !_state;
|
||||
_time = millis();
|
||||
_lastState = _state;
|
||||
_changed = 0;
|
||||
_lastTime = _time;
|
||||
_lastChange = _time;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*
|
||||
* read() returns the state of the button, 1==pressed, 0==released, *
|
||||
* does debouncing, captures and maintains times, previous states, etc. *
|
||||
*----------------------------------------------------------------------*/
|
||||
uint8_t Button::read(void)
|
||||
{
|
||||
static uint32_t ms;
|
||||
static uint8_t pinVal;
|
||||
|
||||
ms = millis();
|
||||
if (_isTouchButton)
|
||||
{
|
||||
pinVal = _touchState;
|
||||
}
|
||||
else
|
||||
{
|
||||
pinVal = digitalRead(_pin);
|
||||
}
|
||||
|
||||
if (_invert != 0)
|
||||
pinVal = !pinVal;
|
||||
if (ms - _lastChange < _dbTime)
|
||||
{
|
||||
_lastTime = _time;
|
||||
_time = ms;
|
||||
_changed = 0;
|
||||
return _state;
|
||||
Serial.println("State is: " + _state);
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastTime = _time;
|
||||
_lastState = _state;
|
||||
_state = pinVal;
|
||||
_time = ms;
|
||||
if (_state != _lastState)
|
||||
{
|
||||
_lastChange = ms;
|
||||
_changed = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_changed = 0;
|
||||
}
|
||||
return _state;
|
||||
#ifdef DEBUG
|
||||
Serial.println("State is: " + _state);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setButton(bool state)
|
||||
{
|
||||
_touchState = state;
|
||||
}
|
||||
|
||||
uint8_t Button::readAxis()
|
||||
{
|
||||
static uint32_t ms;
|
||||
static uint8_t pinVal;
|
||||
static uint16_t val;
|
||||
|
||||
ms = millis();
|
||||
val = analogRead(_pin);
|
||||
|
||||
if (val > 3900)
|
||||
{
|
||||
pinVal = 1;
|
||||
_axis = DPAD_V_FULL;
|
||||
#ifdef DEBUG
|
||||
Serial.println("Value is: " + val);
|
||||
#endif
|
||||
}
|
||||
else if (val > 1500 && val < 2000)
|
||||
{
|
||||
pinVal = 1;
|
||||
_axis = DPAD_V_HALF;
|
||||
#ifdef DEBUG
|
||||
Serial.println("Value is: " + val);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
pinVal = 0;
|
||||
_axis = DPAD_V_NONE;
|
||||
}
|
||||
|
||||
if (_invert == 0)
|
||||
pinVal = !pinVal;
|
||||
if (ms - _lastChange < _dbTime)
|
||||
{
|
||||
_lastTime = _time;
|
||||
_time = ms;
|
||||
_changed = 0;
|
||||
return _state;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastTime = _time;
|
||||
_lastState = _state;
|
||||
_state = pinVal;
|
||||
_time = ms;
|
||||
if (_state != _lastState)
|
||||
{
|
||||
_lastChange = ms;
|
||||
_changed = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_changed = 0;
|
||||
}
|
||||
return _state;
|
||||
}
|
||||
return _state && _changed;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*
|
||||
* isPressed() and isReleased() check the button state when it was last *
|
||||
* read, and return false (0) or true (!=0) accordingly. *
|
||||
* These functions do not cause the button to be read. *
|
||||
*----------------------------------------------------------------------*/
|
||||
uint8_t Button::isPressed(void)
|
||||
{
|
||||
return _state == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
uint8_t Button::isAxisPressed(void)
|
||||
{
|
||||
if (_state)
|
||||
return _axis;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t Button::isReleased(void)
|
||||
{
|
||||
return _state == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*
|
||||
* wasPressed() and wasReleased() check the button state to see if it *
|
||||
* changed between the last two reads and return false (0) or *
|
||||
* true (!=0) accordingly. *
|
||||
* These functions do not cause the button to be read. *
|
||||
*----------------------------------------------------------------------*/
|
||||
uint8_t Button::wasPressed(void)
|
||||
{
|
||||
return _state && _changed;
|
||||
}
|
||||
|
||||
uint8_t Button::wasAxisPressed(void)
|
||||
{
|
||||
if (_state && _changed)
|
||||
return _axis;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t Button::wasReleased(void)
|
||||
{
|
||||
return !_state && _changed;
|
||||
}
|
||||
/*----------------------------------------------------------------------*
|
||||
* pressedFor(ms) and releasedFor(ms) check to see if the button is *
|
||||
* pressed (or released), and has been in that state for the specified *
|
||||
* time in milliseconds. Returns false (0) or true (1) accordingly. *
|
||||
* These functions do not cause the button to be read. *
|
||||
*----------------------------------------------------------------------*/
|
||||
uint8_t Button::pressedFor(uint32_t ms)
|
||||
{
|
||||
return (_state == 1 && _time - _lastChange >= ms) ? 1 : 0;
|
||||
}
|
||||
|
||||
uint8_t Button::releasedFor(uint32_t ms)
|
||||
{
|
||||
return (_state == 0 && _time - _lastChange >= ms) ? 1 : 0;
|
||||
}
|
||||
/*----------------------------------------------------------------------*
|
||||
* lastChange() returns the time the button last changed state, *
|
||||
* in milliseconds. *
|
||||
*----------------------------------------------------------------------*/
|
||||
uint32_t Button::lastChange(void)
|
||||
{
|
||||
return _lastChange;
|
||||
}
|
||||
@@ -5,3 +5,63 @@
|
||||
|
||||
void initButton(void);
|
||||
void handleButton(void);
|
||||
void setButton(uint8_t index, bool state);
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*
|
||||
* Arduino Button Library v1.0 *
|
||||
* Jack Christensen Mar 2012 *
|
||||
* *
|
||||
* This work is licensed under the Creative Commons Attribution- *
|
||||
* ShareAlike 3.0 Unported License. To view a copy of this license, *
|
||||
* visit http://creativecommons.org/licenses/by-sa/3.0/ or send a *
|
||||
* letter to Creative Commons, 171 Second Street, Suite 300, *
|
||||
* San Francisco, California, 94105, USA. *
|
||||
*----------------------------------------------------------------------*/
|
||||
#ifndef Button_h
|
||||
#define Button_h
|
||||
// #if ARDUINO >= 100
|
||||
#include <Arduino.h>
|
||||
// #else
|
||||
// #include <WProgram.h>
|
||||
// #endif
|
||||
|
||||
|
||||
#define DPAD_V_FULL 2
|
||||
#define DPAD_V_HALF 1
|
||||
#define DPAD_V_NONE 0
|
||||
|
||||
class Button
|
||||
{
|
||||
public:
|
||||
Button(uint8_t pin, uint8_t invert, uint32_t dbTime, bool isTouch);
|
||||
uint8_t read();
|
||||
uint8_t readAxis();
|
||||
uint8_t isPressed();
|
||||
uint8_t isAxisPressed();
|
||||
uint8_t isReleased();
|
||||
uint8_t wasPressed();
|
||||
uint8_t wasAxisPressed();
|
||||
uint8_t wasReleased();
|
||||
uint8_t pressedFor(uint32_t ms);
|
||||
uint8_t releasedFor(uint32_t ms);
|
||||
uint32_t lastChange();
|
||||
void setButton(bool state);
|
||||
|
||||
|
||||
private:
|
||||
uint8_t _pin; //arduino pin number
|
||||
uint8_t _puEnable; //internal pullup resistor enabled
|
||||
uint8_t _invert; //if 0, interpret high state as pressed, else interpret low state as pressed
|
||||
uint8_t _state; //current button state
|
||||
uint8_t _lastState; //previous button state
|
||||
uint8_t _changed; //state changed since last read
|
||||
uint8_t _axis; //state changed since last read
|
||||
uint32_t _time; //time of current state (all times are in ms)
|
||||
uint32_t _lastTime; //time of previous state
|
||||
uint32_t _lastChange; //time of last state change
|
||||
uint32_t _dbTime; //debounce time
|
||||
const bool _isTouchButton;
|
||||
bool _touchState;
|
||||
};
|
||||
#endif
|
||||
536
reflow_plate_fw/src/controlloop.cpp
Normal file
536
reflow_plate_fw/src/controlloop.cpp
Normal file
@@ -0,0 +1,536 @@
|
||||
#include "controlloop.h"
|
||||
|
||||
#include "thermo.h"
|
||||
#include "button.h"
|
||||
#include "lcd.h"
|
||||
|
||||
|
||||
|
||||
// Button AXIS_Y = Button(BUTTON_AXIS_Y, true, DEBOUNCE_MS);
|
||||
// Button AXIS_X = Button(BUTTON_AXIS_X, true, DEBOUNCE_MS);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
Title: Reflow Oven Controller
|
||||
Version: 1.20
|
||||
Date: 26-11-2012
|
||||
Company: Rocket Scream Electronics
|
||||
Author: Lim Phang Moh
|
||||
Website: www.rocketscream.com
|
||||
|
||||
Brief
|
||||
=====
|
||||
This is an example firmware for our Arduino compatible reflow oven controller.
|
||||
The reflow curve used in this firmware is meant for lead-free profile
|
||||
(it's even easier for leaded process!). You'll need to use the MAX31855
|
||||
library for Arduino if you are having a shield of v1.60 & above which can be
|
||||
downloaded from our GitHub repository. Please check our wiki
|
||||
(www.rocketscream.com/wiki) for more information on using this piece of code
|
||||
together with the reflow oven controller shield.
|
||||
|
||||
Temperature (Degree Celcius) Magic Happens Here!
|
||||
245-| x x
|
||||
| x x
|
||||
| x x
|
||||
| x x
|
||||
200-| x x
|
||||
| x | | x
|
||||
| x | | x
|
||||
| x | |
|
||||
150-| x | |
|
||||
| x | | |
|
||||
| x | | |
|
||||
| x | | |
|
||||
| x | | |
|
||||
| x | | |
|
||||
| x | | |
|
||||
30 -| x | | |
|
||||
|< 60 - 90 s >|< 90 - 120 s >|< 90 - 120 s >|
|
||||
| Preheat Stage | Soaking Stage | Reflow Stage | Cool
|
||||
0 |_ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
Time (Seconds)
|
||||
|
||||
This firmware owed very much on the works of other talented individuals as
|
||||
follows:
|
||||
==========================================
|
||||
Brett Beauregard (www.brettbeauregard.com)
|
||||
==========================================
|
||||
Author of Arduino PID library. On top of providing industry standard PID
|
||||
implementation, he gave a lot of help in making this reflow oven controller
|
||||
possible using his awesome library.
|
||||
|
||||
==========================================
|
||||
Limor Fried of Adafruit (www.adafruit.com)
|
||||
==========================================
|
||||
Author of Arduino MAX6675 library. Adafruit has been the source of tonnes of
|
||||
tutorials, examples, and libraries for everyone to learn.
|
||||
|
||||
Disclaimer
|
||||
==========
|
||||
Dealing with high voltage is a very dangerous act! Please make sure you know
|
||||
what you are dealing with and have proper knowledge before hand. Your use of
|
||||
any information or materials on this reflow oven controller is entirely at
|
||||
your own risk, for which we shall not be liable.
|
||||
|
||||
Licences
|
||||
========
|
||||
This reflow oven controller hardware and firmware are released under the
|
||||
Creative Commons Share Alike v3.0 license
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
You are free to take this piece of code, use it and modify it.
|
||||
All we ask is attribution including the supporting libraries used in this
|
||||
firmware.
|
||||
|
||||
Required Libraries
|
||||
==================
|
||||
- Arduino PID Library:
|
||||
>> https://github.com/br3ttb/Arduino-PID-Library
|
||||
- MAX31855 Library (for board v1.60 & above):
|
||||
>> https://github.com/rocketscream/MAX31855
|
||||
- MAX6675 Library (for board v1.50 & below):
|
||||
>> https://github.com/adafruit/MAX6675-library
|
||||
|
||||
Revision Description
|
||||
======== ===========
|
||||
1.20 Adds supports for v1.60 (and above) of Reflow Oven Controller
|
||||
Shield:
|
||||
- Uses MAX31855KASA+ chip and pin reassign (allowing A4 & A5 (I2C)
|
||||
to be used for user application).
|
||||
- Uses analog based switch (allowing D2 & D3 to be used for user
|
||||
application).
|
||||
Adds waiting state when temperature too hot to start reflow process.
|
||||
Corrected thermocouple disconnect error interpretation (MAX6675).
|
||||
1.10 Arduino IDE 1.0 compatible.
|
||||
1.00 Initial public release.
|
||||
*******************************************************************************/
|
||||
// Comment either one the following #define to select your board revision
|
||||
// Newer board version starts from v1.60 using MAX31855KASA+ chip
|
||||
|
||||
// ***** LCD MESSAGES *****
|
||||
const char *lcdMessagesReflowStatus[] = {
|
||||
"Ready",
|
||||
"Preheat",
|
||||
"Soaking",
|
||||
"Reflow",
|
||||
"Cooling",
|
||||
"Done",
|
||||
"Warning, HOT!",
|
||||
"Error"};
|
||||
|
||||
// ***** DEGREE SYMBOL FOR LCD *****
|
||||
unsigned char degree[8] = {
|
||||
140, 146, 146, 140, 128, 128, 128, 128};
|
||||
|
||||
// ***** PID CONTROL VARIABLES *****
|
||||
double setpoint;
|
||||
double input;
|
||||
double output;
|
||||
double kp = PID_KP_PREHEAT;
|
||||
double ki = PID_KI_PREHEAT;
|
||||
double kd = PID_KD_PREHEAT;
|
||||
int windowSize;
|
||||
int inputInt;
|
||||
unsigned long windowStartTime;
|
||||
unsigned long nextCheck;
|
||||
unsigned long nextRead;
|
||||
unsigned long timerSoak;
|
||||
unsigned long buzzerPeriod;
|
||||
// Reflow oven controller state machine state variable
|
||||
reflowState_t reflowState;
|
||||
// Reflow oven controller status
|
||||
reflowStatus_t reflowStatus;
|
||||
// Switch debounce state machine state variable
|
||||
debounceState_t debounceState;
|
||||
// Switch debounce timer
|
||||
long lastDebounceTime;
|
||||
// Switch press status
|
||||
switch_t switchStatus;
|
||||
// Seconds timer
|
||||
int timerSeconds;
|
||||
//output state
|
||||
bool outputState = false;
|
||||
|
||||
bool isFault;
|
||||
String activeStatus;
|
||||
int oldTemp = 0;
|
||||
byte state;
|
||||
bool disableMenu;
|
||||
bool profileIsOn;
|
||||
|
||||
// Specify PID control interface & it must be here, after all declaration
|
||||
PID reflowOvenPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);
|
||||
|
||||
void initControlLoop(void)
|
||||
{
|
||||
// Set window size
|
||||
windowSize = 2000;
|
||||
// Initialize time keeping variable
|
||||
nextCheck = millis();
|
||||
// Initialize thermocouple reading variable
|
||||
nextRead = millis();
|
||||
}
|
||||
|
||||
|
||||
String getReflowStatus_str(void)
|
||||
{
|
||||
switch (reflowStatus)
|
||||
{
|
||||
case REFLOW_STATUS_OFF:
|
||||
{
|
||||
return String("Reflow OFF");
|
||||
}
|
||||
break;
|
||||
case REFLOW_STATUS_ON:
|
||||
{
|
||||
return String("Reflow ON");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
return String("Unknow State");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String getReflowState_str(void)
|
||||
{
|
||||
switch (reflowState)
|
||||
{
|
||||
case REFLOW_STATE_IDLE:
|
||||
return String("Idle");
|
||||
break;
|
||||
case REFLOW_STATE_PREHEAT:
|
||||
return String("Preheating");
|
||||
break;
|
||||
case REFLOW_STATE_SOAK:
|
||||
return String("Soaking");
|
||||
break;
|
||||
case REFLOW_STATE_REFLOW:
|
||||
return String("Reflowing");
|
||||
break;
|
||||
case REFLOW_STATE_COOL:
|
||||
return String("Cooling");
|
||||
break;
|
||||
case REFLOW_STATE_COMPLETE:
|
||||
return String("Completed");
|
||||
break;
|
||||
case REFLOW_STATE_TOO_HOT:
|
||||
return String("OVERHEATING!");
|
||||
break;
|
||||
case REFLOW_STATE_ERROR:
|
||||
return String("Error");
|
||||
break;
|
||||
default:
|
||||
return String("Unknow State");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setReflowStatus(bool newStatus)
|
||||
{
|
||||
if (newStatus)
|
||||
{
|
||||
profileIsOn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
profileIsOn = false;
|
||||
reflowStatus = REFLOW_STATUS_OFF;
|
||||
// Reinitialize state machine
|
||||
reflowState = REFLOW_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
reflowStatus_t getReflowStatus(void)
|
||||
{
|
||||
return reflowStatus;
|
||||
}
|
||||
|
||||
double getReflowTargetTemp(void)
|
||||
{
|
||||
return setpoint;
|
||||
}
|
||||
|
||||
bool getOutputState(void)
|
||||
{
|
||||
return outputState;
|
||||
}
|
||||
|
||||
void handleReflowStatemachine(void)
|
||||
{
|
||||
// Reflow oven controller state machine
|
||||
switch (reflowState)
|
||||
{
|
||||
case REFLOW_STATE_IDLE:
|
||||
activeStatus = "Idle";
|
||||
// If oven temperature is still above room temperature
|
||||
if (input >= TEMPERATURE_ROOM)
|
||||
{
|
||||
reflowState = REFLOW_STATE_TOO_HOT;
|
||||
Serial.println("Status: Too hot to start");
|
||||
}
|
||||
else
|
||||
{
|
||||
// If switch is pressed to start reflow process
|
||||
//if (switchStatus == SWITCH_1)
|
||||
if (profileIsOn != 0)
|
||||
{
|
||||
// Send header for CSV file
|
||||
Serial.println("Time Setpoint Input Output");
|
||||
// Intialize seconds timer for serial debug information
|
||||
timerSeconds = 0;
|
||||
// Initialize PID control window starting time
|
||||
windowStartTime = millis();
|
||||
// Ramp up to minimum soaking temperature
|
||||
setpoint = TEMPERATURE_SOAK_MIN;
|
||||
// Tell the PID to range between 0 and the full window size
|
||||
reflowOvenPID.SetOutputLimits(0, windowSize);
|
||||
reflowOvenPID.SetSampleTime(PID_SAMPLE_TIME);
|
||||
// Turn the PID on
|
||||
reflowOvenPID.SetMode(AUTOMATIC);
|
||||
// Proceed to preheat stage
|
||||
reflowState = REFLOW_STATE_PREHEAT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case REFLOW_STATE_PREHEAT:
|
||||
activeStatus = "Preheat";
|
||||
reflowStatus = REFLOW_STATUS_ON;
|
||||
// If minimum soak temperature is achieve
|
||||
if (input >= TEMPERATURE_SOAK_MIN)
|
||||
{
|
||||
// Chop soaking period into smaller sub-period
|
||||
timerSoak = millis() + SOAK_MICRO_PERIOD;
|
||||
// Set less agressive PID parameters for soaking ramp
|
||||
reflowOvenPID.SetTunings(PID_KP_SOAK, PID_KI_SOAK, PID_KD_SOAK);
|
||||
// Ramp up to first section of soaking temperature
|
||||
setpoint = TEMPERATURE_SOAK_MIN + SOAK_TEMPERATURE_STEP;
|
||||
// Proceed to soaking state
|
||||
reflowState = REFLOW_STATE_SOAK;
|
||||
}
|
||||
break;
|
||||
|
||||
case REFLOW_STATE_SOAK:
|
||||
activeStatus = "Soak";
|
||||
// If micro soak temperature is achieved
|
||||
if (millis() > timerSoak)
|
||||
{
|
||||
timerSoak = millis() + SOAK_MICRO_PERIOD;
|
||||
// Increment micro setpoint
|
||||
setpoint += SOAK_TEMPERATURE_STEP;
|
||||
if (setpoint > TEMPERATURE_SOAK_MAX)
|
||||
{
|
||||
// Set agressive PID parameters for reflow ramp
|
||||
reflowOvenPID.SetTunings(PID_KP_REFLOW, PID_KI_REFLOW, PID_KD_REFLOW);
|
||||
// Ramp up to first section of soaking temperature
|
||||
setpoint = TEMPERATURE_REFLOW_MAX;
|
||||
// Proceed to reflowing state
|
||||
reflowState = REFLOW_STATE_REFLOW;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case REFLOW_STATE_REFLOW:
|
||||
activeStatus = "Reflow";
|
||||
// We need to avoid hovering at peak temperature for too long
|
||||
// Crude method that works like a charm and safe for the components
|
||||
if (input >= (TEMPERATURE_REFLOW_MAX - 5))
|
||||
{
|
||||
// Set PID parameters for cooling ramp
|
||||
reflowOvenPID.SetTunings(PID_KP_REFLOW, PID_KI_REFLOW, PID_KD_REFLOW);
|
||||
// Ramp down to minimum cooling temperature
|
||||
setpoint = TEMPERATURE_COOL_MIN;
|
||||
// Proceed to cooling state
|
||||
reflowState = REFLOW_STATE_COOL;
|
||||
}
|
||||
break;
|
||||
|
||||
case REFLOW_STATE_COOL:
|
||||
activeStatus = "Cool";
|
||||
// If minimum cool temperature is achieve
|
||||
if (input <= TEMPERATURE_COOL_MIN)
|
||||
{
|
||||
// Retrieve current time for buzzer usage
|
||||
buzzerPeriod = millis() + 1000;
|
||||
// Turn on buzzer and green LED to indicate completion
|
||||
digitalWrite(BUZZER, HIGH);
|
||||
// Turn off reflow process
|
||||
reflowStatus = REFLOW_STATUS_OFF;
|
||||
// Proceed to reflow Completion state
|
||||
reflowState = REFLOW_STATE_COMPLETE;
|
||||
}
|
||||
break;
|
||||
|
||||
case REFLOW_STATE_COMPLETE:
|
||||
activeStatus = "Complete";
|
||||
if (millis() > buzzerPeriod)
|
||||
{
|
||||
// Turn off buzzer and green LED
|
||||
digitalWrite(BUZZER, LOW);
|
||||
// Reflow process ended
|
||||
reflowState = REFLOW_STATE_IDLE;
|
||||
profileIsOn = 0;
|
||||
Serial.println("Profile is OFF");
|
||||
}
|
||||
break;
|
||||
|
||||
case REFLOW_STATE_TOO_HOT:
|
||||
// If oven temperature drops below room temperature
|
||||
if (input < TEMPERATURE_ROOM)
|
||||
{
|
||||
// Ready to reflow
|
||||
reflowState = REFLOW_STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case REFLOW_STATE_ERROR:
|
||||
// If thermocouple problem is still present
|
||||
|
||||
if (input == 0) // || (input == FAULT_SHORT_GND) ||
|
||||
// (input == FAULT_SHORT_VCC))
|
||||
{
|
||||
// Wait until thermocouple wire is connected
|
||||
reflowState = REFLOW_STATE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear to perform reflow process
|
||||
reflowState = REFLOW_STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleReflowPID(void)
|
||||
{
|
||||
unsigned long now;
|
||||
// PID computation and SSR control
|
||||
if (reflowStatus == REFLOW_STATUS_ON)
|
||||
{
|
||||
now = millis();
|
||||
|
||||
reflowOvenPID.Compute();
|
||||
|
||||
if ((now - windowStartTime) > windowSize)
|
||||
{
|
||||
// Time to shift the Relay Window
|
||||
windowStartTime += windowSize;
|
||||
}
|
||||
if (output > (now - windowStartTime))
|
||||
{
|
||||
digitalWrite(HEAT_OUT, HIGH);
|
||||
outputState = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
digitalWrite(HEAT_OUT, LOW);
|
||||
outputState = false;
|
||||
}
|
||||
}
|
||||
// Reflow oven process is off, ensure oven is off
|
||||
else
|
||||
{
|
||||
digitalWrite(HEAT_OUT, LOW);
|
||||
outputState = false;
|
||||
}
|
||||
}
|
||||
|
||||
void handleTemperatureReadings(void)
|
||||
{
|
||||
unsigned long now;
|
||||
|
||||
// Time to read thermocouple?
|
||||
if (millis() > nextRead)
|
||||
{
|
||||
|
||||
// Read thermocouple next sampling period
|
||||
nextRead += SENSOR_SAMPLING_TIME;
|
||||
// Read current temperature
|
||||
|
||||
input = getTemperature();
|
||||
// Check and print any faults
|
||||
uint8_t fault = getThermoCoupleFault();
|
||||
// inputInt = (int) input;
|
||||
// if ((input <= inputInt) || (input >= inputInt)) {
|
||||
// loopScreen();
|
||||
// }
|
||||
if ((((int)input) < inputInt) || (((int)input) > inputInt))
|
||||
{
|
||||
//loopScreen();
|
||||
}
|
||||
inputInt = input / 1;
|
||||
|
||||
if (oldTemp != inputInt)
|
||||
{
|
||||
if (state == 0)
|
||||
{
|
||||
loopScreen();
|
||||
}
|
||||
#ifdef Serial
|
||||
if ((input > 0) && (input <= 500))
|
||||
{
|
||||
Serial.print("Float temp: " + String(input));
|
||||
Serial.print(" ; ");
|
||||
Serial.println("Integer temp: " + String(inputInt));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// If thermocouple problem detected
|
||||
if (input == 0)
|
||||
{
|
||||
// Illegal operation
|
||||
reflowState = REFLOW_STATE_ERROR;
|
||||
reflowStatus = REFLOW_STATUS_OFF;
|
||||
}
|
||||
oldTemp = inputInt;
|
||||
}
|
||||
|
||||
if (millis() > nextCheck)
|
||||
{
|
||||
// Check input in the next seconds
|
||||
nextCheck += 1000;
|
||||
// If reflow process is on going
|
||||
if (reflowStatus == REFLOW_STATUS_ON)
|
||||
{
|
||||
// Increase seconds timer for reflow curve analysis
|
||||
timerSeconds++;
|
||||
// Send temperature and time stamp to serial
|
||||
Serial.print(timerSeconds);
|
||||
Serial.print(" ");
|
||||
Serial.print(setpoint);
|
||||
Serial.print(" ");
|
||||
Serial.print(input);
|
||||
Serial.print(" ");
|
||||
Serial.println(output);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Turn off red LED
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
}
|
||||
// If currently in error state
|
||||
if (reflowState == REFLOW_STATE_ERROR)
|
||||
{
|
||||
// No thermocouple wire connected
|
||||
Serial.println("TC Error!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---- REFLOW MAIN LOOP ---- */
|
||||
void handleControlLoop()
|
||||
{
|
||||
if (state == 9)
|
||||
{
|
||||
Serial.println("handlecontrolloop: ERROR state");
|
||||
return;
|
||||
}
|
||||
|
||||
handleTemperatureReadings();
|
||||
handleReflowStatemachine();
|
||||
handleReflowPID();
|
||||
}
|
||||
79
reflow_plate_fw/src/controlloop.h
Normal file
79
reflow_plate_fw/src/controlloop.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include <PID_v1.h>
|
||||
#include "Arduino.h"
|
||||
|
||||
// ***** CONSTANTS *****
|
||||
#define TEMPERATURE_ROOM 50
|
||||
#define TEMPERATURE_SOAK_MIN 150
|
||||
#define TEMPERATURE_SOAK_MAX 185
|
||||
#define TEMPERATURE_REFLOW_MAX 220
|
||||
#define TEMPERATURE_COOL_MIN 100
|
||||
#define SENSOR_SAMPLING_TIME 1000
|
||||
#define SOAK_TEMPERATURE_STEP 5
|
||||
#define SOAK_MICRO_PERIOD 9000
|
||||
#define DEBOUNCE_PERIOD_MIN 50
|
||||
#define PREHEAT_PERIOD 12000
|
||||
#define REFLOW_PERIOD 12000
|
||||
#define COOLDOWN_PERIOD 30000
|
||||
|
||||
// ***** PID PARAMETERS *****
|
||||
// ***** PRE-HEAT STAGE *****
|
||||
#define PID_KP_PREHEAT 100
|
||||
#define PID_KI_PREHEAT 0.025
|
||||
#define PID_KD_PREHEAT 20
|
||||
// ***** SOAKING STAGE *****
|
||||
#define PID_KP_SOAK 300
|
||||
#define PID_KI_SOAK 0.05
|
||||
#define PID_KD_SOAK 250
|
||||
// ***** REFLOW STAGE *****
|
||||
#define PID_KP_REFLOW 300
|
||||
#define PID_KI_REFLOW 0.05
|
||||
#define PID_KD_REFLOW 350
|
||||
#define PID_SAMPLE_TIME 1000
|
||||
// This is for testing on different board
|
||||
// #define LCD_PIN 14
|
||||
// #define ODROID
|
||||
|
||||
|
||||
// ***** TYPE DEFINITIONS *****
|
||||
typedef enum REFLOW_STATE
|
||||
{
|
||||
REFLOW_STATE_IDLE,
|
||||
REFLOW_STATE_PREHEAT,
|
||||
REFLOW_STATE_SOAK,
|
||||
REFLOW_STATE_REFLOW,
|
||||
REFLOW_STATE_COOL,
|
||||
REFLOW_STATE_COMPLETE,
|
||||
REFLOW_STATE_TOO_HOT,
|
||||
REFLOW_STATE_ERROR
|
||||
} reflowState_t;
|
||||
|
||||
typedef enum REFLOW_STATUS
|
||||
{
|
||||
REFLOW_STATUS_OFF,
|
||||
REFLOW_STATUS_ON
|
||||
} reflowStatus_t;
|
||||
|
||||
typedef enum SWITCH
|
||||
{
|
||||
SWITCH_NONE,
|
||||
SWITCH_1,
|
||||
SWITCH_2
|
||||
} switch_t;
|
||||
|
||||
typedef enum DEBOUNCE_STATE
|
||||
{
|
||||
DEBOUNCE_STATE_IDLE,
|
||||
DEBOUNCE_STATE_CHECK,
|
||||
DEBOUNCE_STATE_RELEASE
|
||||
} debounceState_t;
|
||||
|
||||
void initControlLoop(void);
|
||||
void handleControlLoop(void);
|
||||
|
||||
String getReflowStatus_str(void);
|
||||
String getReflowState_str(void);
|
||||
reflowStatus_t getReflowStatus(void);
|
||||
void setReflowStatus(bool newStatus);
|
||||
double getReflowTargetTemp(void);
|
||||
bool getOutputState(void);
|
||||
@@ -1,9 +1,17 @@
|
||||
#include "heater.h"
|
||||
|
||||
uint16_t heatingProfile[] =
|
||||
{
|
||||
30,
|
||||
50,
|
||||
70,
|
||||
90
|
||||
};
|
||||
|
||||
void initHeater(void)
|
||||
{
|
||||
|
||||
pinMode(HEAT_OUT,OUTPUT);
|
||||
digitalWrite(HEAT_OUT, LOW);
|
||||
}
|
||||
|
||||
void handleHeater(void)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "lcd.h"
|
||||
|
||||
#include "thermo.h"
|
||||
#include "controlloop.h"
|
||||
#include "button.h"
|
||||
|
||||
#include <TFT_eSPI.h> // Include the graphics library
|
||||
|
||||
@@ -8,56 +10,190 @@ TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
|
||||
TFT_eSprite looptime_spr = TFT_eSprite(&tft);
|
||||
|
||||
//chart data
|
||||
TFT_eSprite chartbg_spr = TFT_eSprite(&tft);
|
||||
TFT_eSprite chartActual_spr = TFT_eSprite(&tft);
|
||||
TFT_eSprite chartTarget_spr = TFT_eSprite(&tft);
|
||||
TFT_eSprite chartXaxis_spr = TFT_eSprite(&tft);
|
||||
TFT_eSprite chartYaxis_spr = TFT_eSprite(&tft);
|
||||
TFT_eSprite chartArea_spr = TFT_eSprite(&tft);
|
||||
TFT_eSprite reflowstate_spr = TFT_eSprite(&tft);
|
||||
TFT_eSprite output_spr = TFT_eSprite(&tft);
|
||||
|
||||
//buttons
|
||||
String buttonNames[] = {"START", "STOP", "SET"};
|
||||
char buttonlabels[BUTTONS_N][10] = {"", "", ""};
|
||||
TFT_eSPI_Button key[BUTTONS_N];
|
||||
|
||||
uint16_t calData[5] = {549, 3080, 343, 3159, 4};
|
||||
|
||||
void prep_chart(void)
|
||||
uint32_t lastButtonTime = 0;
|
||||
|
||||
void updateTemperature(void)
|
||||
{
|
||||
chartbg_spr.createSprite(CHART_W, CHART_H);
|
||||
//draw actualTemperature
|
||||
chartArea_spr.fillRoundRect(TEMP_X, 0, 60-TEMP_R, TEMP_H, TEMP_R, TEMP_BG_COLOR);
|
||||
chartArea_spr.drawString("A:", TEMP_X + TEMP_LABEL_X, TEMP_H / 4);
|
||||
chartArea_spr.drawString("T:", TEMP_X + TEMP_LABEL_X, TEMP_H / 4 * 3);
|
||||
|
||||
chartArea_spr.drawNumber(getTemperature(), TEMP_X + TEMP_VALUE_X, TEMP_H / 4);
|
||||
if (getReflowStatus() == REFLOW_STATUS_OFF)
|
||||
{
|
||||
chartArea_spr.drawString("--", TEMP_X + TEMP_VALUE_X, TEMP_H / 4 * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
chartArea_spr.drawNumber(getReflowTargetTemp(), TEMP_X + TEMP_VALUE_X, TEMP_H / 4 * 3);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t calcTemp(double temp)
|
||||
{
|
||||
//map(long x, long in_min, long in_max, long out_min, long out_max)
|
||||
return CHART_H - map(temp, CHART_TEMP_MIN, CHART_TEMP_MAX, CHART_Y_AXIS_OFFSET, CHART_H) + CHART_X_AXIS_OFFSET;
|
||||
}
|
||||
|
||||
uint32_t calcTime(uint32_t timeMs)
|
||||
{
|
||||
return map((timeMs / 1000), 0, CHART_TIME_MAX, 0, CHART_W - CHART_Y_AXIS_OFFSET);
|
||||
}
|
||||
|
||||
void updateTargetChart(void)
|
||||
{
|
||||
|
||||
chartArea_spr.fillSprite(CHART_BG_COLOR);
|
||||
|
||||
//draw preheat
|
||||
uint32_t stepTimeStart = 0;
|
||||
uint32_t stepTimeStop = calcTime(PREHEAT_PERIOD);
|
||||
chartArea_spr.drawLine(stepTimeStart,
|
||||
CHART_H - CHART_X_AXIS_OFFSET,
|
||||
stepTimeStop,
|
||||
calcTemp(TEMPERATURE_SOAK_MIN),
|
||||
CHART_TARGET_COLOR);
|
||||
//draw soak
|
||||
stepTimeStart = stepTimeStop;
|
||||
stepTimeStop += calcTime(SOAK_MICRO_PERIOD * (((TEMPERATURE_SOAK_MAX - TEMPERATURE_SOAK_MIN) / SOAK_TEMPERATURE_STEP) - 1));
|
||||
chartArea_spr.drawLine(stepTimeStart,
|
||||
calcTemp(TEMPERATURE_SOAK_MIN),
|
||||
stepTimeStop,
|
||||
calcTemp(TEMPERATURE_SOAK_MAX),
|
||||
CHART_TARGET_COLOR);
|
||||
|
||||
//draw reflow
|
||||
stepTimeStart = stepTimeStop;
|
||||
stepTimeStop += calcTime(REFLOW_PERIOD);
|
||||
chartArea_spr.drawLine(stepTimeStart,
|
||||
calcTemp(TEMPERATURE_SOAK_MAX),
|
||||
stepTimeStop,
|
||||
calcTemp(TEMPERATURE_REFLOW_MAX),
|
||||
CHART_TARGET_COLOR);
|
||||
|
||||
//draw cool
|
||||
stepTimeStart = stepTimeStop;
|
||||
stepTimeStop += calcTime(COOLDOWN_PERIOD);
|
||||
chartArea_spr.drawLine(stepTimeStart,
|
||||
calcTemp(TEMPERATURE_REFLOW_MAX),
|
||||
stepTimeStop,
|
||||
calcTemp(TEMPERATURE_ROOM),
|
||||
CHART_TARGET_COLOR);
|
||||
|
||||
updateTemperature();
|
||||
chartArea_spr.pushSprite(CHART_X + CHART_Y_AXIS_OFFSET, CHART_Y);
|
||||
}
|
||||
|
||||
void prepTargetChart(void)
|
||||
{
|
||||
chartArea_spr.createSprite(CHART_W - CHART_Y_AXIS_OFFSET, CHART_H - CHART_X_AXIS_OFFSET);
|
||||
chartArea_spr.setTextFont(TEMP_FONT);
|
||||
chartArea_spr.setTextColor(TEMP_F_COLOR);
|
||||
chartArea_spr.setTextDatum(CL_DATUM);
|
||||
updateTargetChart();
|
||||
}
|
||||
|
||||
void updateReflowState(void)
|
||||
{
|
||||
reflowstate_spr.fillRoundRect(STATE_OFFSET, 0, (STATE_W / 2) - (2 * STATE_OFFSET), STATE_H, STATE_R, STATE_BG_COLOR);
|
||||
reflowstate_spr.fillRoundRect(STATE_W / 2 + STATE_OFFSET, 0, (STATE_W / 2) - (2 * STATE_OFFSET), STATE_H, STATE_R, STATE_BG_COLOR);
|
||||
reflowstate_spr.drawString(getReflowState_str(), STATE_W / 4, STATE_H / 2);
|
||||
reflowstate_spr.drawString(getReflowStatus_str(), STATE_W / 4 * 3, STATE_H / 2);
|
||||
reflowstate_spr.pushSprite(STATE_X, STATE_Y);
|
||||
}
|
||||
|
||||
void prepReflowstate(void)
|
||||
{
|
||||
reflowstate_spr.createSprite(STATE_W, STATE_H);
|
||||
reflowstate_spr.setTextFont(STATE_FONT);
|
||||
reflowstate_spr.setTextColor(STATE_F_COLOR);
|
||||
reflowstate_spr.setTextDatum(MC_DATUM);
|
||||
updateReflowState();
|
||||
}
|
||||
|
||||
void updateOutput(void)
|
||||
{
|
||||
output_spr.fillSprite(OUTPUT_BG);
|
||||
if (getOutputState())
|
||||
{
|
||||
output_spr.fillCircle(OUTPUT_W / 2, OUTPUT_H / 2, OUTPUT_R, OUTPUT_ON_COLOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
output_spr.drawCircle(OUTPUT_W / 2, OUTPUT_H / 2, OUTPUT_R, OUTPUT_OFF_COLOR);
|
||||
}
|
||||
output_spr.pushSprite(OUTPUT_X, OUTPUT_Y);
|
||||
}
|
||||
|
||||
void prepOutput(void)
|
||||
{
|
||||
output_spr.createSprite(OUTPUT_W, OUTPUT_H);
|
||||
updateOutput();
|
||||
}
|
||||
|
||||
void prepChart(void)
|
||||
{
|
||||
chartXaxis_spr.createSprite(CHART_W, CHART_X_AXIS_OFFSET);
|
||||
chartYaxis_spr.createSprite(CHART_Y_AXIS_OFFSET, CHART_H);
|
||||
//draw X-axis
|
||||
chartbg_spr.drawLine(0, CHART_H - CHART_X_AXIS_OFFSET, CHART_W, CHART_H - CHART_X_AXIS_OFFSET, CHART_LINE_COLOR);
|
||||
chartXaxis_spr.drawLine(0, 0, CHART_W, 0, CHART_LINE_COLOR);
|
||||
//draw Y-axis
|
||||
chartbg_spr.drawLine(CHART_Y_AXIS_OFFSET, 0, CHART_Y_AXIS_OFFSET, CHART_H, CHART_LINE_COLOR);
|
||||
chartYaxis_spr.drawLine(CHART_Y_AXIS_OFFSET - 1, 0, CHART_Y_AXIS_OFFSET - 1, CHART_H, CHART_LINE_COLOR);
|
||||
|
||||
//draw Y axis labels
|
||||
uint16_t tickIndex = (CHART_H - CHART_X_AXIS_OFFSET) / CHART_Y_TICKS;
|
||||
chartbg_spr.setTextColor(CHART_TEXT_COLOR);
|
||||
for (int i = 0; i < CHART_Y_TICKS + 1; i++)
|
||||
chartXaxis_spr.setTextColor(CHART_TEXT_COLOR);
|
||||
chartYaxis_spr.setTextColor(CHART_TEXT_COLOR);
|
||||
|
||||
for (int i = 0; i < CHART_Y_TICKS; i++)
|
||||
{
|
||||
//tick value
|
||||
uint16_t y_tick_step = CHART_TEMP_MAX - ((CHART_TEMP_MAX - CHART_TEMP_MIN) / CHART_Y_TICKS * i) - CHART_TEMP_MIN;
|
||||
chartbg_spr.drawLine(CHART_Y_AXIS_OFFSET - 2, tickIndex * (i + 1), CHART_Y_AXIS_OFFSET + 2, tickIndex * (i + 1), CHART_LINE_COLOR);
|
||||
chartbg_spr.setTextDatum(BR_DATUM);
|
||||
chartbg_spr.drawString(String(y_tick_step), CHART_Y_AXIS_OFFSET - 3, tickIndex * (i + 1), CHART_FONT);
|
||||
uint16_t y_tick_step = CHART_TEMP_MAX - ((CHART_TEMP_MAX - CHART_TEMP_MIN) / CHART_Y_TICKS * i + 1) + CHART_TEMP_MIN;
|
||||
chartYaxis_spr.drawLine(CHART_Y_AXIS_OFFSET - 8, tickIndex * (i + 1), CHART_Y_AXIS_OFFSET, tickIndex * (i + 1), CHART_LINE_COLOR);
|
||||
chartYaxis_spr.setTextDatum(BR_DATUM);
|
||||
chartYaxis_spr.drawString(String(y_tick_step), CHART_Y_AXIS_OFFSET - 3, tickIndex * (i + 1), CHART_FONT);
|
||||
}
|
||||
|
||||
//draw X axis labels
|
||||
tickIndex = (CHART_W - CHART_Y_AXIS_OFFSET) / CHART_X_TICKS;
|
||||
for (int i = 0; i < CHART_X_TICKS + 1; i++)
|
||||
{
|
||||
uint16_t x_tick_step = (CHART_TIME_MAX / CHART_Y_TICKS * i);
|
||||
chartbg_spr.drawLine(tickIndex * i + CHART_Y_AXIS_OFFSET, CHART_H - CHART_X_AXIS_OFFSET + 2, tickIndex * i + CHART_Y_AXIS_OFFSET, CHART_H - CHART_X_AXIS_OFFSET - 2, CHART_LINE_COLOR);
|
||||
chartbg_spr.setTextDatum(TR_DATUM);
|
||||
chartbg_spr.drawString(String(x_tick_step),tickIndex * i + CHART_Y_AXIS_OFFSET,CHART_H - CHART_X_AXIS_OFFSET + 3,CHART_FONT);
|
||||
uint16_t x_tick_step = (CHART_TIME_MAX / CHART_X_TICKS * (i));
|
||||
chartXaxis_spr.drawLine(tickIndex * i + CHART_Y_AXIS_OFFSET - 1, 0, tickIndex * i + CHART_Y_AXIS_OFFSET - 1, 8, CHART_LINE_COLOR);
|
||||
chartXaxis_spr.setTextDatum(TR_DATUM);
|
||||
chartXaxis_spr.drawString(String(x_tick_step), tickIndex * i + CHART_Y_AXIS_OFFSET - 1, 3, CHART_FONT);
|
||||
}
|
||||
chartXaxis_spr.pushSprite(CHART_X, CHART_Y + CHART_H - CHART_X_AXIS_OFFSET);
|
||||
chartYaxis_spr.pushSprite(CHART_X, CHART_Y);
|
||||
}
|
||||
|
||||
chartbg_spr.pushSprite(CHART_X, CHART_Y);
|
||||
}
|
||||
|
||||
void prep_sprite(void)
|
||||
void prepStatus(void)
|
||||
{
|
||||
looptime_spr.createSprite(20, tft.fontHeight(1) + 1);
|
||||
}
|
||||
|
||||
void prep_buttons(void)
|
||||
void updateStatus(void)
|
||||
{
|
||||
looptime_spr.fillSprite(TFT_BLACK);
|
||||
looptime_spr.setTextDatum(MC_DATUM);
|
||||
looptime_spr.drawNumber(getLooptime(), looptime_spr.width() / 2, looptime_spr.height() / 2, 1);
|
||||
looptime_spr.pushSprite(1, 1);
|
||||
}
|
||||
|
||||
void prepButtons(void)
|
||||
{
|
||||
const uint32_t button_width = tft.width() / BUTTONS_N;
|
||||
|
||||
@@ -113,6 +249,9 @@ void touch_calibrate()
|
||||
}
|
||||
|
||||
void updateGUIButtons(void)
|
||||
{
|
||||
uint32_t timeNow = millis();
|
||||
if (timeNow - lastButtonTime > BUTTON_INTERVAL)
|
||||
{
|
||||
uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
|
||||
bool pressed = tft.getTouch(&t_x, &t_y);
|
||||
@@ -121,11 +260,15 @@ void updateGUIButtons(void)
|
||||
{
|
||||
if (pressed && key[b].contains(t_x, t_y))
|
||||
{
|
||||
//Serial.println("key pressed");
|
||||
key[b].press(true); // tell the button it is pressed
|
||||
setButton(b, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Serial.println("key released");
|
||||
key[b].press(false); // tell the button it is NOT pressed
|
||||
setButton(b, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +287,8 @@ void updateGUIButtons(void)
|
||||
key[b].drawButton(false, buttonNames[b]);
|
||||
}
|
||||
}
|
||||
lastButtonTime = timeNow;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -158,20 +303,36 @@ void initLCD(void)
|
||||
tft.invertDisplay(false);
|
||||
//touch_calibrate();
|
||||
tft.setTouch(calData);
|
||||
prep_sprite();
|
||||
prep_buttons();
|
||||
prep_chart();
|
||||
|
||||
prepStatus();
|
||||
prepButtons();
|
||||
prepChart();
|
||||
prepReflowstate();
|
||||
prepTargetChart();
|
||||
prepOutput();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Main loop
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
uint32_t lastLCDupdate = 0;
|
||||
|
||||
void handleLCD()
|
||||
{
|
||||
looptime_spr.fillSprite(TFT_BLACK);
|
||||
looptime_spr.setTextDatum(MC_DATUM);
|
||||
looptime_spr.drawNumber(getLooptime(), looptime_spr.width() / 2, looptime_spr.height() / 2, 1);
|
||||
looptime_spr.pushSprite(1, 1);
|
||||
updateStatus();
|
||||
|
||||
uint32_t timeNow = millis();
|
||||
if (timeNow - lastLCDupdate > LCD_INTERVAL)
|
||||
{
|
||||
updateGUIButtons();
|
||||
//tft.drawString(showValue("Temp", getTemperature(), "grC"), 10, 10);
|
||||
updateReflowState();
|
||||
updateTargetChart();
|
||||
updateOutput();
|
||||
lastLCDupdate = timeNow;
|
||||
}
|
||||
}
|
||||
|
||||
void loopScreen(void)
|
||||
{
|
||||
}
|
||||
@@ -3,32 +3,77 @@
|
||||
|
||||
#include "status.h"
|
||||
|
||||
// #define WIDTH 240
|
||||
// #define HEIGHT 60
|
||||
#define TFT_WIDTH 240
|
||||
#define TFT_HEIGT 320
|
||||
#define TFT_DEFAULT_R 4
|
||||
|
||||
#define LCD_INTERVAL 100
|
||||
|
||||
#define BUTTON_H 60
|
||||
#define BUTTON_W 240
|
||||
#define BUTTON_W TFT_WIDTH
|
||||
#define BUTTONS_N 3
|
||||
#define BUTTON_PADDING 2
|
||||
#define BUTTON_RADIUS 8
|
||||
#define BUTTON_COLOR TFT_BLUE
|
||||
#define BUTTON_INTERVAL 20
|
||||
|
||||
#define KEY_TEXTSIZE 1 // Font size multiplier
|
||||
|
||||
#define CHART_X 0
|
||||
#define CHART_Y 30
|
||||
#define CHART_W 240
|
||||
#define CHART_H 200
|
||||
#define CHART_FONT 1
|
||||
#define CHART_Y 40
|
||||
#define CHART_Y_AXIS_OFFSET 24
|
||||
#define CHART_X_AXIS_OFFSET 10
|
||||
#define CHART_TIME_MAX 360 //time scale in seconds
|
||||
#define CHART_W TFT_WIDTH - 5
|
||||
#define CHART_H 200
|
||||
#define CHART_FONT 1
|
||||
|
||||
#define CHART_TIME_MAX 140 //time scale in seconds
|
||||
#define CHART_TEMP_MIN 20 //offset in degrees
|
||||
#define CHART_TEMP_MAX 360 //degrees
|
||||
#define CHART_Y_TICKS 10
|
||||
#define CHART_X_TICKS 10
|
||||
#define CHART_TEMP_MAX 240 //degrees
|
||||
#define CHART_Y_TICKS 11
|
||||
#define CHART_X_TICKS 7
|
||||
#define CHART_LINE_COLOR TFT_WHITE
|
||||
#define CHART_TEXT_COLOR TFT_RED
|
||||
#define CHART_TARGET_COLOR TFT_WHITE
|
||||
#define CHART_ACTUAL_COLOR TFT_RED
|
||||
#define CHART_BG_COLOR TFT_BLACK
|
||||
|
||||
|
||||
#define STATE_X 0
|
||||
#define STATE_Y 13
|
||||
#define STATE_W TFT_WIDTH
|
||||
#define STATE_H 24
|
||||
#define STATE_FONT 2
|
||||
#define STATE_OFFSET 3
|
||||
#define STATE_F_COLOR TFT_BLACK
|
||||
#define STATE_R TFT_DEFAULT_R
|
||||
#define STATE_BG_COLOR TFT_GREEN
|
||||
|
||||
#define TEMP_W 60
|
||||
#define TEMP_H 40
|
||||
#define TEMP_X CHART_W - TEMP_W - 20//allign right
|
||||
#define TEMP_Y 40
|
||||
#define TEMP_FONT 2
|
||||
#define TEMP_F_COLOR TFT_WHITE
|
||||
#define TEMP_BG_COLOR TFT_BLUE
|
||||
#define TEMP_R TFT_DEFAULT_R
|
||||
#define TEMP_LABEL_X 2
|
||||
#define TEMP_VALUE_X 30
|
||||
|
||||
#define OUTPUT_W 12
|
||||
#define OUTPUT_H 12
|
||||
#define OUTPUT_R 5
|
||||
#define OUTPUT_Y 0
|
||||
#define OUTPUT_X TFT_WIDTH - OUTPUT_W - 2
|
||||
#define OUTPUT_ON_COLOR TFT_RED
|
||||
#define OUTPUT_OFF_COLOR TFT_WHITE
|
||||
#define OUTPUT_BG TFT_BLACK
|
||||
|
||||
|
||||
|
||||
#define DEBOUNCE_MS 100
|
||||
|
||||
void initLCD(void);
|
||||
void handleLCD(void);
|
||||
|
||||
void loopScreen(void);
|
||||
|
||||
@@ -6,26 +6,26 @@
|
||||
#include "button.h"
|
||||
#include "heater.h"
|
||||
#include "status.h"
|
||||
#include "controlloop.h"
|
||||
|
||||
void setup()
|
||||
{
|
||||
// put your setup code here, to run once:
|
||||
initStatus();
|
||||
initLCD();
|
||||
initThermo();
|
||||
initButton();
|
||||
initHeater();
|
||||
|
||||
initControlLoop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
// put your main code here, to run repeatedly:
|
||||
handleLCD();
|
||||
handleThermo();
|
||||
handleButton();
|
||||
handleHeater();
|
||||
handleStatus();
|
||||
handleControlLoop();
|
||||
}
|
||||
@@ -6,14 +6,12 @@ uint32_t thermo_lastTime = 0;
|
||||
|
||||
double lastTemperature = 0;
|
||||
|
||||
// the setup function runs once when you press reset or power the board
|
||||
void initThermo()
|
||||
{
|
||||
Thermocouple *originThermocouple = new MAX6675_Thermocouple(THERM_CL, THERM_CS, THERM_SO);
|
||||
thermocouple = new SmoothThermocouple(originThermocouple, SMOOTHING_FACTOR);
|
||||
}
|
||||
|
||||
// the loop function runs over and over again forever
|
||||
void handleThermo(void)
|
||||
{
|
||||
// Reads temperature
|
||||
@@ -21,13 +19,32 @@ void handleThermo(void)
|
||||
if (timeNow - thermo_lastTime > THERMO_INTERVAL)
|
||||
{
|
||||
lastTemperature = thermocouple->readCelsius();
|
||||
Serial.printf("Thermocouple = %d\n",lastTemperature);
|
||||
thermo_lastTime = timeNow;
|
||||
}
|
||||
|
||||
//delay(100); // optionally, only to delay the output of information in the example.
|
||||
}
|
||||
|
||||
double getTemperature(void)
|
||||
{
|
||||
return lastTemperature;
|
||||
return 23;//lastTemperature;
|
||||
}
|
||||
|
||||
bool getThermoCoupleFault(void)
|
||||
{
|
||||
#ifdef MAX31856
|
||||
uint8_t fault = thermocouple.readFault();
|
||||
if (fault)
|
||||
{
|
||||
if (fault & MAX6675_FAULT_CJRANGE) //Serial.println("Cold Junction Range Fault");
|
||||
if (fault & MAX31856_FAULT_TCRANGE) //Serial.println("Thermocouple Range Fault");
|
||||
if (fault & MAX31856_FAULT_CJHIGH) //Serial.println("Cold Junction High Fault");
|
||||
if (fault & MAX31856_FAULT_CJLOW) //Serial.println("Cold Junction Low Fault");
|
||||
if (fault & MAX31856_FAULT_TCHIGH) //Serial.println("Thermocouple High Fault");
|
||||
if (fault & MAX31856_FAULT_TCLOW) //Serial.println("Thermocouple Low Fault");
|
||||
if (fault & MAX31856_FAULT_OVUV) //Serial.println("Over/Under Voltage Fault");
|
||||
if (fault & MAX31856_FAULT_OPEN) //Serial.println("Thermocouple Open Fault");
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
#include <SmoothThermocouple.h>
|
||||
|
||||
#define THERMO_INTERVAL 200
|
||||
#define SMOOTHING_FACTOR 5
|
||||
#define SMOOTHING_FACTOR 2
|
||||
|
||||
void initThermo(void);
|
||||
void handleThermo(void);
|
||||
double getTemperature(void);
|
||||
bool getThermoCoupleFault(void);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user