Merge branch 'lcd2'

This commit is contained in:
2023-02-17 20:55:48 +01:00
34 changed files with 542554 additions and 202 deletions

Binary file not shown.

BIN
CAD/3D/LCD.SLDPRT Normal file

Binary file not shown.

394634
CAD/3D/OUTPUT/UM2_case.gcode Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

126492
CAD/3D/OUTPUT/case.STL Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

BIN
CAD/3D/PSU_5V.SLDPRT Normal file

Binary file not shown.

BIN
CAD/3D/SSR40.SLDPRT Normal file

Binary file not shown.

Binary file not shown.

BIN
CAD/3D/case.SLDPRT Normal file

Binary file not shown.

BIN
CAD/3D/dril_template.SLDDRW Normal file

Binary file not shown.

Binary file not shown.

BIN
CAD/Datasheet/MAX31855.pdf Normal file

Binary file not shown.

3
reflow_plate_fw/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"C_Cpp.errorSquiggles": "Disabled"
}

View File

@@ -0,0 +1,99 @@
///////////////////////////////////////////////////
// Setup for STM32 Nucleo and ILI9341 display //
///////////////////////////////////////////////////
// Last update by Bodmer: 28/11/19
// STM32 optimised functions are not yet compatible with STM32H743 processor.
// The STM32H743 does work with the slower generic processor drivers
//
// REMINDER - Nucleo-F743ZI and Nucleo-F743ZI2 have different pin port allocations
// and require appropriate selection in IDE. ^---- Note the extra 2 in part number!
// Define STM32 to invoke STM32 optimised driver
#define STM32
// Define the TFT display driver
#define ILI9341_DRIVER
//#define ILI9481_DRIVER
// MOSI and SCK do not need to be defined, connect:
// - Arduino SCK to TFT SCK
// - Arduino MOSI to TFT SDI(may be marked SDA or MOSI)
// Typical Arduino SPI port 1 pins are (SCK=D13, MISO=D12, MOSI=D11) this is port pins PA5, PA6 and PA7 on Nucleo-F767ZI
// SPI port 2 pins are (SCK=D18, MISO=A7, MOSI=D17) this is port pins PB13, PC2 and PB15 on Nucleo-F767ZI
//#define TFT_MOSI PA7
//#define TFT_SCLK PA5
#define TFT_DC PB1
#define TFT_RST PA8
#define TFT_CS PB0
#define TFT_SPI_PORT 1 // SPI 1 maximum clock rate is 55MHz
#define TFT_MOSI PA7
#define TFT_MISO PA6
#define TFT_SCLK PA5
/*
#define TFT_SPI_PORT 2 // SPI 2 maximum clock rate is 27MHz
#define TFT_MOSI D17
#define TFT_MISO A7
#define TFT_SCLK D18
//*/
/*
#define TFT_SPI_PORT 2 // SPI 2 maximum clock rate is 27MHz
#define TFT_MOSI PB15
#define TFT_MISO PC2
#define TFT_SCLK PB13
//*/
/*
#define TFT_SPI_PORT 2 // SPI 2 maximum clock rate is 27MHz
#define TFT_MOSI PB15
#define TFT_MISO PB14
#define TFT_SCLK PB13
//*/
// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
// #define TFT_CS D5 // Chip select control pin to TFT CS
// #define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
// #define TFT_RST D7 // Reset pin to TFT RST (or RESET)
// Alternatively, we can use STM32 port reference names PXnn
//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5
//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6
//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset
// Use an Arduino pin for initial testing as connecting to processor reset
// may not work (pulse too short at power up?)
// Chip select for XPT2046 touch controller
#define TOUCH_CS PB2
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
// STM32 support for smooth fonts via program memory (FLASH) arrays
#define SMOOTH_FONT
#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)
#define SPI_TOUCH_FREQUENCY 2500000 // Must be very slow
// This has no effect, transactions for STM32 are automatically enabled
#define SUPPORT_TRANSACTIONS

View File

@@ -1,39 +0,0 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@@ -1,33 +0,0 @@
// See SetupX_Template.h for all options available
#define SSD1351_DRIVER
#define TFT_WIDTH 128
#define TFT_HEIGHT 128
#define STM32
#define TFT_SPI_PORT 1
#define SSD1351_1DOT5_INCH_128 // For 128 x 128 display
//#define TFT_MOSI PA7
//#define TFT_SCLK PA5
#define TFT_DC PB1
#define TFT_RST PA12
#define TFT_CS PB0
#define LOAD_GLCD // Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
#define LOAD_FONT8 // Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts- 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
#define SMOOTH_FONT
#define SPI_FREQUENCY 20000000
//#define SPI_FREQUENCY 40000000 // Works after shielding the wires!

View File

@@ -0,0 +1,277 @@
#include "ft6206_touch.h"
/**************************************************************************/
/*!
@brief Instantiates a new FT6206 class
*/
/**************************************************************************/
// I2C, no address adjustments or pins
FT6206::FT6206() { touches = 0; }
/**************************************************************************/
/*!
@brief Setups the I2C interface and hardware, identifies if chip is found
@param thresh Optional threshhold-for-touch value, default is
FT6206_DEFAULT_THRESSHOLD but you can try changing it if your screen is
too/not sensitive.
@returns True if an FT6206 is found, false on any failure
*/
/**************************************************************************/
// bool begin(uint8_t thresh = FT62XX_DEFAULT_THRESHOLD, uint16_t width, uint16_t height, bool flip );
bool FT6206::begin(uint8_t sda, uint8_t scl, uint16_t width, uint16_t height, bool flip, uint8_t thresh) {
Wire.begin(sda, scl);
_width = width;
_height = height;
_flip = flip;
#ifdef FT6206_DEBUG
Serial.print("Vend ID: 0x");
Serial.println(readRegister8(FT62XX_REG_VENDID), HEX);
Serial.print("Chip ID: 0x");
Serial.println(readRegister8(FT62XX_REG_CHIPID), HEX);
Serial.print("Firm V: ");
Serial.println(readRegister8(FT62XX_REG_FIRMVERS));
Serial.print("Point Rate Hz: ");
Serial.println(readRegister8(FT62XX_REG_POINTRATE));
Serial.print("Thresh: ");
Serial.println(readRegister8(FT62XX_REG_THRESHHOLD));
// dump all registers
for (int16_t i = 0; i < 0x10; i++) {
Serial.print("I2C $");
Serial.print(i, HEX);
Serial.print(" = 0x");
Serial.println(readRegister8(i), HEX);
}
#endif
// change threshhold to be higher/lower
writeRegister8(FT62XX_REG_THRESHHOLD, thresh);
if (readRegister8(FT62XX_REG_VENDID) != FT62XX_VENDID) {
return false;
}
uint8_t id = readRegister8(FT62XX_REG_CHIPID);
if ((id != FT6206_CHIPID) && (id != FT6236_CHIPID) &&
(id != FT6236U_CHIPID)) {
return false;
}
return true;
}
/**************************************************************************/
/*!
@brief Determines if there are any touches detected
@returns Number of touches detected, can be 0, 1 or 2
*/
/**************************************************************************/
uint8_t FT6206::touched(void) {
uint8_t n = readRegister8(FT62XX_REG_NUMTOUCHES);
if (n > 2) {
n = 0;
}
return n;
}
/**************************************************************************/
/*!
@brief Queries the chip and retrieves a point data
@param n The # index (0 or 1) to the points we can detect. In theory we can
detect 2 points but we've found that you should only use this for
single-touch since the two points cant share the same half of the screen.
@returns {@link TS_Point} object that has the x and y coordinets set. If the
z coordinate is 0 it means the point is not touched. If z is 1, it is
currently touched.
*/
/**************************************************************************/
TS_Point FT6206::getPoint(uint8_t n) {
readData();
if ((touches == 0) || (n > 1)) {
return TS_Point(0, 0, 0);
} else {
if(_flip)
{
return TS_Point(_width - touchX[n], _height - touchY[n], 1);
}
else
{
return TS_Point(touchX[n], touchY[n], 1);
}
}
}
/************ lower level i/o **************/
/**************************************************************************/
/*!
@brief Reads the bulk of data from captouch chip. Fill in {@link touches},
{@link touchX}, {@link touchY} and {@link touchID} with results
*/
/**************************************************************************/
void FT6206::readData(void) {
uint8_t i2cdat[16];
Wire.beginTransmission(FT62XX_ADDR);
Wire.write((byte)0);
Wire.endTransmission();
Wire.requestFrom((byte)FT62XX_ADDR, (byte)16);
for (uint8_t i = 0; i < 16; i++)
i2cdat[i] = Wire.read();
#ifdef FT6206_DEBUG
for (int16_t i = 0; i < 16; i++) {
Serial.print("I2C $");
Serial.print(i, HEX);
Serial.print(" = 0x");
Serial.println(i2cdat[i], HEX);
}
#endif
touches = i2cdat[0x02];
if ((touches > 2) || (touches == 0)) {
touches = 0;
}
#ifdef FT6206_DEBUG
Serial.print("# Touches: ");
Serial.println(touches);
for (uint8_t i = 0; i < 16; i++) {
Serial.print("0x");
Serial.print(i2cdat[i], HEX);
Serial.print(" ");
}
Serial.println();
if (i2cdat[0x01] != 0x00) {
Serial.print("Gesture #");
Serial.println(i2cdat[0x01]);
}
#endif
for (uint8_t i = 0; i < 2; i++) {
touchX[i] = i2cdat[0x03 + i * 6] & 0x0F;
touchX[i] <<= 8;
touchX[i] |= i2cdat[0x04 + i * 6];
touchY[i] = i2cdat[0x05 + i * 6] & 0x0F;
touchY[i] <<= 8;
touchY[i] |= i2cdat[0x06 + i * 6];
touchID[i] = i2cdat[0x05 + i * 6] >> 4;
}
#ifdef FT6206_DEBUG
Serial.println();
for (uint8_t i = 0; i < touches; i++) {
Serial.print("ID #");
Serial.print(touchID[i]);
Serial.print("\t(");
Serial.print(touchX[i]);
Serial.print(", ");
Serial.print(touchY[i]);
Serial.print(") ");
}
Serial.println();
#endif
}
uint8_t FT6206::readRegister8(uint8_t reg) {
uint8_t x;
// use i2c
Wire.beginTransmission(FT62XX_ADDR);
Wire.write((byte)reg);
Wire.endTransmission();
Wire.requestFrom((byte)FT62XX_ADDR, (byte)1);
x = Wire.read();
#ifdef I2C_DEBUG
Serial.print("$");
Serial.print(reg, HEX);
Serial.print(": 0x");
Serial.println(x, HEX);
#endif
return x;
}
void FT6206::writeRegister8(uint8_t reg, uint8_t val) {
// use i2c
Wire.beginTransmission(FT62XX_ADDR);
Wire.write((byte)reg);
Wire.write((byte)val);
Wire.endTransmission();
}
/*
// DONT DO THIS - REALLY - IT DOESNT WORK
void FT6206::autoCalibrate(void) {
writeRegister8(FT06_REG_MODE, FT6206_REG_FACTORYMODE);
delay(100);
//Serial.println("Calibrating...");
writeRegister8(FT6206_REG_CALIBRATE, 4);
delay(300);
for (uint8_t i = 0; i < 100; i++) {
uint8_t temp;
temp = readRegister8(FT6206_REG_MODE);
Serial.println(temp, HEX);
//return to normal mode, calibration finish
if (0x0 == ((temp & 0x70) >> 4))
break;
}
delay(200);
//Serial.println("Calibrated");
delay(300);
writeRegister8(FT6206_REG_MODE, FT6206_REG_FACTORYMODE);
delay(100);
writeRegister8(FT6206_REG_CALIBRATE, 5);
delay(300);
writeRegister8(FT6206_REG_MODE, FT6206_REG_WORKMODE);
delay(300);
}
*/
/****************/
/**************************************************************************/
/*!
@brief Instantiates a new FT6206 class with x, y and z set to 0 by default
*/
/**************************************************************************/
TS_Point::TS_Point(void) { x = y = z = 0; }
/**************************************************************************/
/*!
@brief Instantiates a new FT6206 class with x, y and z set by params.
@param _x The X coordinate
@param _y The Y coordinate
@param _z The Z coordinate
*/
/**************************************************************************/
TS_Point::TS_Point(int16_t _x, int16_t _y, int16_t _z) {
x = _x;
y = _y;
z = _z;
}
/**************************************************************************/
/*!
@brief Simple == comparator for two TS_Point objects
@returns True if x, y and z are the same for both points, False otherwise.
*/
/**************************************************************************/
bool TS_Point::operator==(TS_Point p1) {
return ((p1.x == x) && (p1.y == y) && (p1.z == z));
}
/**************************************************************************/
/*!
@brief Simple != comparator for two TS_Point objects
@returns False if x, y and z are the same for both points, True otherwise.
*/
/**************************************************************************/
bool TS_Point::operator!=(TS_Point p1) {
return ((p1.x != x) || (p1.y != y) || (p1.z != z));
}

View File

@@ -0,0 +1,74 @@
#pragma once
#include "Arduino.h"
#include <Wire.h>
#define FT62XX_ADDR 0x38 //!< I2C address
#define FT62XX_G_FT5201ID 0xA8 //!< FocalTech's panel ID
#define FT62XX_REG_NUMTOUCHES 0x02 //!< Number of touch points
#define FT62XX_NUM_X 0x33 //!< Touch X position
#define FT62XX_NUM_Y 0x34 //!< Touch Y position
#define FT62XX_REG_MODE 0x00 //!< Device mode, either WORKING or FACTORY
#define FT62XX_REG_CALIBRATE 0x02 //!< Calibrate mode
#define FT62XX_REG_WORKMODE 0x00 //!< Work mode
#define FT62XX_REG_FACTORYMODE 0x40 //!< Factory mode
#define FT62XX_REG_THRESHHOLD 0x80 //!< Threshold for touch detection
#define FT62XX_REG_POINTRATE 0x88 //!< Point rate
#define FT62XX_REG_FIRMVERS 0xA6 //!< Firmware version
#define FT62XX_REG_CHIPID 0xA3 //!< Chip selecting
#define FT62XX_REG_VENDID 0xA8 //!< FocalTech's panel ID
#define FT62XX_VENDID 0x11 //!< FocalTech's panel ID
#define FT6206_CHIPID 0x06 //!< Chip selecting
#define FT6236_CHIPID 0x36 //!< Chip selecting
#define FT6236U_CHIPID 0x64 //!< Chip selecting
// calibrated for Adafruit 2.8" ctp screen
#define FT62XX_DEFAULT_THRESHOLD 128 //!< Default threshold for touch detection
/**************************************************************************/
/*!
@brief Helper class that stores a TouchScreen Point with x, y, and z
coordinates, for easy math/comparison
*/
/**************************************************************************/
class TS_Point {
public:
TS_Point(void);
TS_Point(int16_t x, int16_t y, int16_t z);
bool operator==(TS_Point);
bool operator!=(TS_Point);
int16_t x; /*!< X coordinate */
int16_t y; /*!< Y coordinate */
int16_t z; /*!< Z coordinate (often used for pressure) */
};
/**************************************************************************/
/*!
@brief Class that stores state and functions for interacting with FT6206
capacitive touch chips
*/
/**************************************************************************/
class FT6206 {
public:
FT6206(void);
bool begin(uint8_t sda, uint8_t scl, uint16_t width, uint16_t height, bool flip = false, uint8_t thresh = FT62XX_DEFAULT_THRESHOLD);
uint8_t touched(void);
TS_Point getPoint(uint8_t n = 0);
// void autoCalibrate(void);
private:
uint16_t _width, _height;
bool _flip;
void writeRegister8(uint8_t reg, uint8_t val);
uint8_t readRegister8(uint8_t reg);
void readData(void);
uint8_t touches;
uint16_t touchX[2], touchY[2], touchID[2];
};

View File

@@ -14,12 +14,16 @@ board = blackpill_f411ce
framework = arduino
upload_protocol = stlink
debug_tool = stlink
monitor_speed = 115200
monitor_port = /dev/cu.usbmodem3474359635391
lib_deps =
yuriisalimov/MAX6675_Thermocouple@^2.0.2
bodmer/TFT_eSPI@^2.3.70
br3ttb/PID@^1.2.1
robtillaart/MAX31855@^0.2.5
;adafruit/Adafruit FT6206 Library@^1.0.6
lib_ldf_mode = deep+
build_flags =
build_flags =
-D USER_SETUP_LOADED=1
-include include/Setup202_SSD1351_128.h
-include include/ILI9341_STM32.h
-D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC
-D PIO_FRAMEWORK_ARDUINO_USB_FULLSPEED_FULLMODE

View File

@@ -1,20 +1,24 @@
#pragma once
#define LCD_MOSI PA7
#define LCD_SCLK PA5
#define LCD_RES PA12
#define LCD_DC PB1
#define LCD_CS PB0
// #define LCD_MOSI PA7
// #define LCD_SCLK PA5
// #define LCD_RES PA8
// #define LCD_DC PB1
// #define LCD_CS PB0
#define THERM_CS PB4
#define THERM_SO PB5
#define THERM_CL PB6
#define THERM_SS PB12
#define THERM_MISO PB14
#define THERM_CLK PB13
#define THERM_MOSI PB15 //not used
#define BUt_LEFT PB15
#define BUt_RIGHT PA10
#define BUT_UP PA11
#define BUT_DOWN PA9
#define BUT_CENTER PA8
#define HEAT_OUT PA2
#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

View File

@@ -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;
}

View File

@@ -4,4 +4,64 @@
#include "board.h"
void initButton(void);
void handleButton(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

View File

@@ -0,0 +1,589 @@
#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;
uint32_t processStartTime = 0;
// 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();
pinMode(HEAT_OUT, OUTPUT);
}
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("HOT!");
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;
}
uint32_t safetyTimer = 0;
#define SAFETYTIMEOUT 5000 //ms
double oldinput = 0;
#define SAFETYMARGIN 5 //degrees C
bool SafetyError = false;
bool getSafetyCheck(void)
{
uint32_t timeNow = millis();
if (!outputState)
{
safetyTimer = timeNow;
oldinput = input;
return true;
}
else
{
if (timeNow - safetyTimer > SAFETYTIMEOUT)
{
if (input - oldinput < SAFETYMARGIN)
{
setReflowStatus(false);
return false;
}
else
{
return true;
oldinput = input;
}
}
}
return true;
}
float getProcessTime(void)
{
if ((reflowStatus == REFLOW_STATUS_ON) && (processStartTime > 0))
{
float retval = (millis() - processStartTime);
retval /= 1000;
return retval;
}
else
{
return 0;
}
}
bool getOverheating(void)
{
return (reflowState == REFLOW_STATE_TOO_HOT);
}
void handleReflowStatemachine(void)
{
// Reflow oven controller state machine
switch (reflowState)
{
case REFLOW_STATE_IDLE:
activeStatus = "Idle";
processStartTime = 0;
// 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 + 5;
// 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;
// log process start time
processStartTime = millis();
}
}
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;
}
//getSafetyCheck();
handleTemperatureReadings();
handleReflowStatemachine();
handleReflowPID();
}

View File

@@ -0,0 +1,79 @@
#pragma once
#include <PID_v1.h>
#include "Arduino.h"
// ***** CONSTANTS *****
#define TEMPERATURE_ROOM 75
#define SENSOR_SAMPLING_TIME 1000
#define DEBOUNCE_PERIOD_MIN 50
// ***** PARAMETERS *****
// ***** PRE-HEAT STAGE *****
#define PID_KP_PREHEAT 100
#define PID_KI_PREHEAT 0.025
#define PID_KD_PREHEAT 20
#define PREHEAT_PERIOD 20000
// ***** SOAKING STAGE *****
#define PID_KP_SOAK 300
#define PID_KI_SOAK 0.05
#define PID_KD_SOAK 250
#define TEMPERATURE_SOAK_MIN 150
#define TEMPERATURE_SOAK_MAX 185
#define SOAK_TEMPERATURE_STEP 5
#define SOAK_MICRO_PERIOD 9000
// ***** REFLOW STAGE *****
#define PID_KP_REFLOW 300
#define PID_KI_REFLOW 0.05
#define PID_KD_REFLOW 350
#define TEMPERATURE_REFLOW_MAX 220
#define REFLOW_PERIOD 30000
// ***** COOLING STAGE *****
#define COOLDOWN_PERIOD 180000
#define TEMPERATURE_COOL_MIN 100
//PID settings
#define PID_SAMPLE_TIME 1000
// ***** 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);
float getProcessTime(void);
bool getOverheating(void);

View File

@@ -1,12 +0,0 @@
#include "heater.h"
void initHeater(void)
{
}
void handleHeater(void)
{
}

View File

@@ -1,7 +0,0 @@
#pragma once
#include "Arduino.h"
#include "board.h"
void initHeater(void);
void handleHeater(void);

View File

@@ -1,42 +1,417 @@
#include "lcd.h"
#include "thermo.h"
#include "controlloop.h"
#include "button.h"
#include <vector>
#include <TFT_eSPI.h> // Include the graphics library
#include "ft6206_touch.h"
TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
FT6206 ts = FT6206();
TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
TFT_eSprite looptime_spr = TFT_eSprite(&tft);
//chart data
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};
uint32_t lastButtonTime = 0;
void updateTemperature(void)
{
//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.drawFloat(getTemperature(), 1, 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_X_AXIS_OFFSET, CHART_H) + CHART_X_AXIS_OFFSET;
}
uint32_t calcTempY(uint32_t inputY)
{
return map(inputY, CHART_H, CHART_X_AXIS_OFFSET, CHART_TEMP_MIN, CHART_TEMP_MAX);
}
uint32_t calcTime(uint32_t timeMs)
{
return map((timeMs / 1000), 0, CHART_TIME_MAX, 0, CHART_W - CHART_Y_AXIS_OFFSET);
}
std::vector<float> temperatureReading;
uint32_t lastReading = 0;
#define TEMPINTERVAL 1000
void updateRealtimeGraph(void)
{
if (getReflowStatus() == REFLOW_STATUS_OFF)
{
temperatureReading.clear();
return;
}
//record temperature
uint32_t timeNow = millis();
if (timeNow - lastReading > TEMPINTERVAL)
{
temperatureReading.push_back(getTemperature());
lastReading = timeNow;
}
//draw graph
uint32_t timeIndex = 0;
uint32_t lastX = 0;
uint32_t lastY = 0;
for (auto &&sample : temperatureReading)
{
uint32_t nowX = calcTime(timeIndex * TEMPINTERVAL);
uint32_t nowY = calcTemp(sample);
chartArea_spr.drawLine(lastX, lastY, nowX, nowY, CHART_ACTUAL_COLOR);
lastX = nowX;
lastY = nowY;
timeIndex++;
}
}
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);
updateRealtimeGraph();
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)
{
uint32_t statusColor = STATE_BG_COLOR;
if (getOverheating())
{
statusColor = TFT_RED;
}
reflowstate_spr.fillRoundRect(STATE_OFFSET, 0, (STATE_W / 2) - (2 * STATE_OFFSET), STATE_H, STATE_R, statusColor);
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
chartXaxis_spr.drawLine(0, 0, CHART_W, 0, CHART_LINE_COLOR);
//draw Y-axis
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;
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 = calcTempY(tickIndex * (i + 1)); //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_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);
}
void prepStatus(void)
{
looptime_spr.createSprite(80, tft.fontHeight(1) + 1);
}
void updateStatus(void)
{
looptime_spr.fillSprite(TFT_BLACK);
looptime_spr.setTextDatum(TL_DATUM);
looptime_spr.setTextColor(TFT_WHITE);
looptime_spr.drawNumber(getLooptime(), 1, 1, 1);
looptime_spr.drawFloat(getProcessTime(), 2, 20, 1, 1);
looptime_spr.pushSprite(1, 1);
}
void prepButtons(void)
{
const uint32_t button_width = tft.width() / BUTTONS_N;
for (int i = 0; i < BUTTONS_N; i++)
{
key[i].initButtonUL(&tft,
(button_width * i) + BUTTON_PADDING,
tft.height() - BUTTON_H,
button_width - (BUTTON_PADDING * 2),
BUTTON_H,
TFT_BLUE, // Outline
TFT_SKYBLUE, // Fill
TFT_BLACK, // Text
buttonlabels[i], // 10 Byte Label
KEY_TEXTSIZE);
key[i].setLabelDatum(0, 2, MC_DATUM);
key[i].drawButton(false, buttonNames[i]);
}
}
#if defined(TOUCH_CS)
void touch_calibrate()
{
uint16_t calData[5];
uint8_t calDataOK = 0;
// Calibrate
tft.fillScreen(TFT_BLACK);
tft.setCursor(20, 0);
tft.setTextFont(2);
tft.setTextSize(1);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.println("Touch corners as indicated");
tft.setTextFont(1);
tft.println();
tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15);
// Use this calibration code in setup():");
// uint16_t calData[5] = { 0,0,0,0,0 };
// tft.setTouch(calData);
//String calDatastr = "";
for (uint8_t i = 0; i < 5; i++)
{
tft.printf("%d,", calData[i]);
}
while (1)
;
}
#endif
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 = ts.touched(); //.getTouch(&t_x, &t_y);
if (pressed)
{
TS_Point p = ts.getPoint();
t_x = p.x;
t_y = p.y;
}
for (uint8_t b = 0; b < BUTTONS_N; b++)
{
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);
}
}
// Check if any key has changed state
for (uint8_t b = 0; b < BUTTONS_N; b++)
{
// If button was just pressed, redraw inverted button
if (key[b].justPressed())
{
key[b].drawButton(true, buttonNames[b]);
}
// If button was just released, redraw normal color button
if (key[b].justReleased())
{
key[b].drawButton(false, buttonNames[b]);
}
}
lastButtonTime = timeNow;
}
}
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------
void initLCD(void) {
void initTouchScreen(void)
{
//ts.begin();
if (!ts.begin(TOUCH_SDA, TOUCH_SCL, tft.width(), tft.height(), true, 40))
{
Serial.println("Unable to start touchscreen.");
}
else
{
Serial.println("Touchscreen started.");
}
}
void initLCD(void)
{
tft.init();
tft.setRotation(TFT_ROTATION);
tft.setTextFont(2);
tft.fillScreen(TFT_BLACK);
tft.invertDisplay(false);
//tft.setTextColor(TFT_WHITE);
tft.setRotation(0);
#if defined(TOUCH_CS)
//touch_calibrate();
tft.setTouch(calData);
#endif
initTouchScreen();
}
String showValue(String designator, float value, String unit)
{
String text;
//text.clear();
if (designator != "")
{
text = designator;
text += " ";
}
text += value;
text += " ";
text += unit;
return text;
prepStatus();
prepButtons();
prepChart();
prepReflowstate();
prepTargetChart();
prepOutput();
}
// -------------------------------------------------------------------------
// Main loop
// -------------------------------------------------------------------------
uint32_t lastLCDupdate = 0;
void handleLCD()
{
tft.drawString(showValue("Temp", getTemperature(), "grC"), 10, 10);
updateStatus();
uint32_t timeNow = millis();
if (timeNow - lastLCDupdate > LCD_INTERVAL)
{
updateGUIButtons();
updateReflowState();
updateTargetChart();
updateOutput();
lastLCDupdate = timeNow;
}
}
void loopScreen(void)
{
}

View File

@@ -1,9 +1,79 @@
#include "Arduino.h"
#include "board.h"
#include "Setup202_SSD1351_128.h"
#include <TFT_eSPI.h> // Include the graphics library
#include "status.h"
#define TFT_WIDTH 240
#define TFT_HEIGT 320
#define TFT_DEFAULT_R 4
#define TFT_ROTATION 0 //2 = upsidedown
#define LCD_INTERVAL 100
#define BUTTON_H 60
#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 40
#define CHART_Y_AXIS_OFFSET 24
#define CHART_X_AXIS_OFFSET 10
#define CHART_W TFT_WIDTH - 5
#define CHART_H 200
#define CHART_FONT 1
#define CHART_TIME_MAX 500 //time scale in seconds
#define CHART_TEMP_MIN 20 //offset in degrees
#define CHART_TEMP_MAX 240 //degrees
#define CHART_Y_TICKS 10
#define CHART_X_TICKS 7
#define CHART_LINE_COLOR TFT_WHITE
#define CHART_TEXT_COLOR TFT_RED
#define CHART_TARGET_COLOR TFT_LIGHTGREY
#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 20
#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);

View File

@@ -4,29 +4,25 @@
#include "lcd.h"
#include "thermo.h"
#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();
pinMode(LED_BUILTIN, OUTPUT);
initControlLoop();
}
void loop()
{
// put your main code here, to run repeatedly:
handleLCD();
handleThermo();
handleButton();
handleHeater();
digitalWrite(LED_BUILTIN, 1);
delay(500);
digitalWrite(LED_BUILTIN, 0);
delay(500);
handleStatus();
handleControlLoop();
}

View File

@@ -0,0 +1,41 @@
#include "status.h"
#include "controlloop.h"
uint64_t timelast = 0;
uint64_t blinkrate = 500;
bool ledstate = false;
uint32_t looptime = 0;
uint32_t looptimelast = 0;
void initStatus(void)
{
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
delay(500);
Serial.println("Heat Plate controller V1");
}
void handleStatus(void)
{
uint64_t timenow = millis();
if(timenow - timelast > blinkrate)
{
timelast = timenow;
ledstate = !ledstate;
digitalWrite(LED_BUILTIN, ledstate);
}
looptime = timenow - looptimelast;
looptimelast = timenow;
}
uint32_t getLooptime(void)
{
return looptime;
}
double getReflowTime(void)
{
return getProcessTime();
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "Arduino.h"
#include "board.h"
void initStatus(void);
void handleStatus(void);
uint32_t getLooptime(void);

View File

@@ -1,46 +1,73 @@
#include "thermo.h"
#include "controlloop.h"
Thermocouple* thermocouple = NULL;
//MAX6675 thermocouple(THERM_SS, THERM_MISO, THERM_CLK);
MAX31855 thermocouple(THERM_CLK, THERM_SS, THERM_MISO); //uint8_t CS, uint8_t MISO);
// the setup function runs once when you press reset or power the board
void initThermo() {
// initialize the Thermocouple
//Adafruit_MAX31855 thermocouple(THERM_CLK, THERM_SS, THERM_MISO);
Thermocouple* originThermocouple = new MAX6675_Thermocouple(THERM_CL, THERM_CS, THERM_SO);
thermocouple = new AverageThermocouple(
originThermocouple,
READINGS_NUMBER,
DELAY_TIME
);
uint32_t thermo_lastTime = 0;
/* OR
thermocouple = new AverageThermocouple(
new MAX6675_Thermocouple(SCK_PIN, CS_PIN, SO_PIN),
READINGS_NUMBER,
DELAY_TIME
);
*/
}
float lastTemperature = 0;
float internal = 0;
// the loop function runs over and over again forever
void handleThermo(void) {
// Reads temperature
const double celsius = thermocouple->readCelsius();
const double kelvin = thermocouple->readKelvin();
const double fahrenheit = thermocouple->readFahrenheit();
bool simulation = false;
#define SIM_TEMP_STEP 1
#define SIM_TEMP_COOL 0.08
#define SIM_INTERVAL 100
uint32_t simTimer = 0;
uint32_t sampleTimer = 0;
// Output of information
Serial.print("Temperature: ");
Serial.print(celsius);
Serial.print(" C, ");
Serial.print(kelvin);
Serial.print(" K, ");
Serial.print(fahrenheit);
Serial.println(" F");
delay(100); // optionally, only to delay the output of information in the example.
}
double getTemperature(void)
void initThermo()
{
return thermocouple->readCelsius();
thermocouple.begin();
if (simulation)
{
lastTemperature = TEMPERATURE_ROOM - 20;
}
}
void handleThermo(void)
{
uint32_t timeNow = millis();
if (simulation)
{
if (timeNow - simTimer > SIM_INTERVAL)
{
if (getOutputState())
{
lastTemperature += SIM_TEMP_STEP;
}
else
{
if (lastTemperature > TEMPERATURE_ROOM - 20)
{
lastTemperature -= SIM_TEMP_COOL;
}
}
simTimer = timeNow;
}
}
else
{
if (timeNow - sampleTimer > THERMO_INTERVAL)
{
int state = thermocouple.read();
lastTemperature = thermocouple.getTemperature();
internal = thermocouple.getInternal();
sampleTimer = timeNow;
}
}
}
float getTemperature(void)
{
return lastTemperature; //lastTemperature;
}
bool getThermoCoupleFault(void)
{
return thermocouple.genericError();
}

View File

@@ -2,26 +2,15 @@
#include "Arduino.h"
#include "board.h"
#include <Thermocouple.h>
#include <MAX6675_Thermocouple.h>
#include <AverageThermocouple.h>
//#include "max6675.h"
#include "MAX31855.h"
/**
How many readings are taken to determine a mean temperature.
The more values, the longer a calibration is performed,
but the readings will be more accurate.
*/
#define READINGS_NUMBER 10
/**
Delay time between a temperature readings
from the temperature sensor (ms).
*/
#define DELAY_TIME 10
#define THERMO_INTERVAL 200
#define SMOOTHING_FACTOR 2
void initThermo(void);
void handleThermo(void);
double getTemperature(void);
float getTemperature(void);
bool getThermoCoupleFault(void);