// Libraries
#include <U8glib.h> // https://github.com/olikraus/u8glib
#include <PID_v1.h> // https://github.com/wagiminator/ATmega-Soldering-Station/blob/master/software/libraries/Arduino-PID-Library.zip
//#include <GyverPID.h>
// (old cpp version of https://github.com/mblythe86/C-PID-Library/tree/master/PID_v1)
#include <EEPROM.h> // for storing user settings into EEPROM
#include <avr/sleep.h> // for sleeping during ADC sampling
// Firmware version
#define VERSION "v1.9"
// Type of MOSFET
#define N_MOSFET // P_MOSFET or N_MOSFET
// Type of OLED Controller
#define SSD1306 // SSD1306 or SH1106
// Type of rotary encoder
#define ROTARY_TYPE 1 // 0: 2 increments/step; 1: 4 increments/step (default)
// Pins
#define SENSOR_PIN A0 // tip temperature sense
#define VIN_PIN A1 // input voltage sense
#define BUZZER_PIN 5 // buzzer
#define BUTTON_PIN 6 // rotary encoder switch
#define ROTARY_1_PIN 7 // rotary encoder 1
#define ROTARY_2_PIN 8 // rotary encoder 2
#define CONTROL_PIN 9 // heater MOSFET PWM control
#define SWITCH_PIN 10 // handle vibration switch
// Default temperature control values (recommended soldering temperature: 300-380°C)
#define TEMP_MIN 150 // min selectable temperature
#define TEMP_MAX 400 // max selectable temperature
#define TEMP_DEFAULT 320 // default start setpoint
#define TEMP_SLEEP 150 // temperature in sleep mode
#define TEMP_BOOST 50 // temperature increase in boost mode
#define TEMP_STEP 10 // rotary encoder temp change steps
// Default tip temperature calibration values
#define TEMP200 216 // temperature at ADC = 200
#define TEMP280 308 // temperature at ADC = 280
#define TEMP360 390 // temperature at ADC = 360
#define TEMPCHP 30 // chip temperature while calibration
#define TIPMAX 8 // max number of tips
#define TIPNAMELENGTH 6 // max length of tip names (including termination)
#define TIPNAME "BC1.5" // default tip name
// Default timer values (0 = disabled)
#define TIME2SLEEP 5 // time to enter sleep mode in minutes
#define TIME2OFF 15 // time to shut off heater in minutes
#define TIMEOFBOOST 40 // time to stay in boost mode in seconds
// Control values
#define TIME2SETTLE 950 // time in microseconds to allow OpAmp output to settle
#define SMOOTHIE 0.05 // OpAmp output smooth factor (1=no smoothing; 0.05 default)
#define PID_ENABLE false // enable PID control
#define BEEP_ENABLE true // enable/disable buzzer
#define BODYFLIP false // enable/disable screen flip
#define ECREVERSE false // enable/disable rotary encoder reverse
#define MAINSCREEN 0 // type of main screen (0: big numbers; 1: more infos)
// EEPROM identifier
#define EEPROM_IDENT 0xE76C // to identify if EEPROM was written by this program
// MOSFET control definitions
#if defined (P_MOSFET) // P-Channel MOSFET
#define HEATER_ON 255
#define HEATER_OFF 0
#define HEATER_PWM 255 - Output
#elif defined (N_MOSFET) // N-Channel MOSFET
#define HEATER_ON 0
#define HEATER_OFF 255
#define HEATER_PWM Output
#else
#error Wrong MOSFET type!
#endif
// Define the aggressive and conservative PID tuning parameters
double aggKp = 11, aggKi = 0.5, aggKd = 1;
double consKp = 11, consKi = 3, consKd = 5;
// Default values that can be changed by the user and stored in the EEPROM
uint16_t DefaultTemp = TEMP_DEFAULT;
uint16_t SleepTemp = TEMP_SLEEP;
uint8_t BoostTemp = TEMP_BOOST;
uint8_t time2sleep = TIME2SLEEP;
uint8_t time2off = TIME2OFF;
uint8_t timeOfBoost = TIMEOFBOOST;
uint8_t MainScrType = MAINSCREEN;
bool PIDenable = PID_ENABLE;
bool beepEnable = BEEP_ENABLE;
bool BodyFlip = BODYFLIP;
bool ECReverse = ECREVERSE;
// Default values for tips
uint16_t CalTemp[TIPMAX][4] = {TEMP200, TEMP280, TEMP360, TEMPCHP};
char TipName[TIPMAX][TIPNAMELENGTH] = {TIPNAME};
uint8_t CurrentTip = 0;
uint8_t NumberOfTips = 1;
// Menu items
const char *SetupItems[] = { "Setup Menu", "Tip Settings", "Temp Settings",
"Timer Settings", "Control Type", "Main Screen",
"Buzzer", "Screen Flip", "EC Reverse", "Information", "Return"
};
const char *TipItems[] = { "Tip:", "Change Tip", "Calibrate Tip",
"Rename Tip", "Delete Tip", "Add new Tip", "Return"
};
const char *TempItems[] = { "Temp Settings", "Default Temp", "Sleep Temp",
"Boost Temp", "Return"
};
const char *TimerItems[] = { "Timer Settings", "Sleep Timer", "Off Timer",
"Boost Timer", "Return"
};
const char *ControlTypeItems[] = { "Control Type", "Direct", "PID" };
const char *MainScreenItems[] = { "Main Screen", "Big Numbers", "More Infos" };
const char *StoreItems[] = { "Store Settings ?", "No", "Yes" };
const char *SureItems[] = { "Are you sure ?", "No", "Yes" };
const char *BuzzerItems[] = { "Buzzer", "Disable", "Enable" };
const char *FlipItems[] = { "Screen Flip", "Disable", "Enable" };
const char *ECReverseItems[] = { "EC Reverse", "Disable", "Enable" };
const char *DefaultTempItems[] = { "Default Temp", "\xB0""C" };
const char *SleepTempItems[] = { "Sleep Temp", "\xB0""C" };
const char *BoostTempItems[] = { "Boost Temp", "\xB0""C" };
const char *SleepTimerItems[] = { "Sleep Timer", "Minutes" };
const char *OffTimerItems[] = { "Off Timer", "Minutes" };
const char *BoostTimerItems[] = { "Boost Timer", "Seconds" };
const char *DeleteMessage[] = { "Warning", "You cannot", "delete your", "last tip!" };
const char *MaxTipMessage[] = { "Warning", "You reached", "maximum number", "of tips!" };
// Variables for pin change interrupt
volatile uint8_t a0, b0, c0, d0;
volatile bool ab0;
volatile int count, countMin, countMax, countStep;
volatile bool handleMoved;
// Variables for temperature control
uint16_t SetTemp, ShowTemp, gap, Step;
double Input, Output, Setpoint, RawTemp, CurrentTemp, ChipTemp;
// Variables for voltage readings
uint16_t Vcc, Vin;
// State variables
bool inSleepMode = false;
bool inOffMode = false;
bool inBoostMode = false;
bool inCalibMode = false;
bool isWorky = true;
bool beepIfWorky = true;
bool TipIsPresent = true;
// Timing variables
uint32_t sleepmillis;
uint32_t boostmillis;
uint32_t buttonmillis;
uint8_t goneMinutes;
uint8_t goneSeconds;
uint8_t SensorCounter = 255;
// Specify variable pointers and initial PID tuning parameters
PID ctrl(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, REVERSE);
// Setup u8g object depending on OLED controller
#if defined (SSD1306)
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST);
#elif defined (SH1106)
U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_FAST | U8G_I2C_OPT_NO_ACK);
#else
#error Wrong OLED controller type!
#endif
// Новые определения цвета
#define TFT_BLACK 0x0000 /* 0, 0, 0 */
#define TFT_NAVY 0x000F /* 0, 0, 128 */
#define TFT_DARKGREEN 0x03E0 /* 0, 128, 0 */
#define TFT_DARKCYAN 0x03EF /* 0, 128, 128 */
#define TFT_MAROON 0x7800 /* 128, 0, 0 */
#define TFT_PURPLE 0x780F /* 128, 0, 128 */
#define TFT_OLIVE 0x7BE0 /* 128, 128, 0 */
#define TFT_LIGHTGREY 0xC618 /* 192, 192, 192 */
#define TFT_DARKGREY 0x7BEF /* 128, 128, 128 */
#define TFT_BLUE 0x001F /* 0, 0, 255 */
#define TFT_GREEN 0x07E0 /* 0, 255, 0 */
#define TFT_CYAN 0x07FF /* 0, 255, 255 */
#define TFT_RED 0xF800 /* 255, 0, 0 */
#define TFT_MAGENTA 0xF81F /* 255, 0, 255 */
#define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */
#define TFT_WHITE 0xFFFF /* 255, 255, 255 */
#define TFT_ORANGE 0xFD20 /* 255, 165, 0 */
#define TFT_GREENYELLOW 0xAFE5 /* 173, 255, 47 */
#define TFT_PINK 0xF81F
////////////////////////////////////////////////////////////////////////////////
///////////////////// Объявление пользовательских функций //////////////////////
////////////////////////////////////////////////////////////////////////////////
/*
void ROTARYCheck();
void SLEEPCheck();
void SENSORCheck();
void calculateTemp();
void Thermostat(); */
void beep();
void setRotary(int rmin, int rmax, int rstep, int rvalue);
int getRotary();
/*
void getEEPROM();
void updateEEPROM();
void SetFlip();
void MainScreen();
void SetupScreen();
void TipScreen();
void TempScreen();
void TimerScreen();
uint8_t MenuScreen(const char *Items[], uint8_t numberOfItems, uint8_t selected);
void MessageScreen(const char *Items[], uint8_t numberOfItems);
uint16_t InputScreen(const char *Items[]);
void InfoScreen();
void ChangeTipScreen();
void CalibrationScreen();
void InputNameScreen();
void DeleteTipScreen();
void AddTipScreen();
uint16_t denoiseAnalog(byte port);
double getChipTemp();
uint16_t getVCC();
uint16_t getVIN();
*/
ISR(PCINT0_vect);
/*
////////////// menuLinearOptiPROGMEM ////////////////////////////
void printGUI();
void printFromPGM(int charMap);
void smartArrow(bool state);
////////////// menuLinearOptiPROGMEM ////////////////////////////
// пример с оптимизированным линейным меню
// оптимизировано очищение дисплея
// названия засунуты в PROGMEM вместо оперативки
// менять значения можно нажатым поворотом, или КЛИКНУТЬ, курсор изменится, и крутить
// значение меняется быстрее при быстром повороте
#define LINES 4 // количество строк дисплея
#define SETTINGS_AMOUNT 7 // количество настроек
#define FAST_STEP 5 // скорость изменения при быстром повороте
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4); // адрес 0x27 или 0x3f
#include <GyverEncoder.h>
//Encoder enc(2, 3, 4); // uno - для работы c кнопкой. CLK, DT, SW
//Encoder enc(7, 8, 6); // nano - для работы c кнопкой. CLK, DT, SW
Encoder enc(A3, A2, A1); // nano - для работы c кнопкой. CLK, DT, SW
int vals[SETTINGS_AMOUNT]; // массив параметров
int8_t arrowPos = 0;
bool controlState = 0; // клик
// названия параметров
const char name1[] PROGMEM = "Speed";
const char name2[] PROGMEM = "Temp";
const char name3[] PROGMEM = "Mode";
const char name4[] PROGMEM = "Direction";
const char name5[] PROGMEM = "Limit";
const char name6[] PROGMEM = "Color";
const char name7[] PROGMEM = "Timeout";
// объявляем таблицу ссылок
const char* const names[] PROGMEM = {
name1, name2, name3, name4, name5, name6, name7,
};
////////////// menuLinearOptiPROGMEM ////////////////////////////
*/
////////////// scrollpage_param_progmem ////////////////////////////
uint8_t scrollpage_param_progmem (uint8_t selected);
void printPointer(uint8_t pointer);
void printMenuItem(uint8_t num);
void FrameSelection (uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t selectcolor);
void editParameter();
/*
Пример меню с вертикальным скроллингом
при достижении указателем последней строки
и прямым управлением параметрами
*/
#define OLED_SOFT_BUFFER_64 // Буфер на стороне МК
#include <GyverOLED.h> // Библиотека дисплея
GyverOLED<SSH1106_128x64> oled; // Обьект дисплея
//#include <GyverButton.h> // Либа кнопок
//GButton up(3, HIGH_PULL); // Кнопки
//GButton down(4, HIGH_PULL);
//GButton ok(2, HIGH_PULL);
//GButton up(A7, HIGH_PULL); // Кнопки
//GButton down(A6, HIGH_PULL);
//GButton ok(A0, HIGH_PULL);
#define ITEMS 21 // Общее кол во пунктов
#include <Adafruit_GFX.h> // Core graphics library
//#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include "Adafruit_ILI9341.h"
#include <SPI.h>
#include <GyverEncoder.h>
//Encoder enc(2, 3, 4); // uno - для работы c кнопкой. CLK, DT, SW
//Encoder enc(7, 8, 6); // nano - для работы c кнопкой. CLK, DT, SW
Encoder enc(A3, A2, A1); // nano - для работы c кнопкой. CLK, DT, SW
// software SPI:
#define TFT_CS 4 // CS Chip select control pin
#define TFT_DC 3 // DC Data Command control pin
#define TFT_RST 2 // RES Reset pin (could connect to Arduino RESET pin)
#define TFT_MOSI 11 // SDA
#define TFT_CLK 13 // SCL
// Создаем объект tft
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
/* массивы строк с именами пунктов */
/*
const char i0[] PROGMEM = " Parameter 0:"; // Каждое имя в своей строке, символ переноса не нужен
const char i1[] PROGMEM = " Parameter 1:";
const char i2[] PROGMEM = " Parameter 2:";
const char i3[] PROGMEM = " Parameter 3:";
const char i4[] PROGMEM = " Parameter 4:";
const char i5[] PROGMEM = " Parameter 5:";
const char i6[] PROGMEM = " Parameter 6:";
const char i7[] PROGMEM = " Parameter 7:";
const char i8[] PROGMEM = " Parameter 8:";
const char i9[] PROGMEM = " Parameter 9:";
const char i10[] PROGMEM = " Parameter 10:";
const char i11[] PROGMEM = " Parameter 11:";
const char* const names[] PROGMEM = // Создаем массив ссылок, обращение к названию пунктов по их номеру
{
i0, i1, i2, i3,
i4, i5, i6, i7,
i8, i9, i10, i11
};
*/
/* массивы строк с именами пунктов */
//const char i0[] PROGMEM = " 123456789012345678901234"; // Каждое имя в своей строке, символ переноса не нужен
const char i0[] PROGMEM = " 01. Standby "; // Каждое имя в своей строке, символ переноса не нужен
const char i1[] PROGMEM = " 02. Sleep ";
const char i2[] PROGMEM = " 03. Boost ";
const char i3[] PROGMEM = " 04. Cold End Adj ";
const char i4[] PROGMEM = " 05. Tip ";
const char i5[] PROGMEM = " 06. Stepping ";
const char i6[] PROGMEM = " 07. Password ";
const char i7[] PROGMEM = " 08. Screen Saver ";
const char i8[] PROGMEM = " 09. Buzzer ";
const char i9[] PROGMEM = " 10. Voltage ";
const char i10[] PROGMEM = " 11. LowVol-P ";
const char i11[] PROGMEM = " 12. Power On State ";
const char i12[] PROGMEM = " 13. Desolder ";
const char i13[] PROGMEM = " 14. Pump set ";
const char i14[] PROGMEM = " 15. Language ";
const char i15[] PROGMEM = " 16. Date Time ";
const char i16[] PROGMEM = " 17. RTC Adjust ";
const char i17[] PROGMEM = " 18. RTC Init ";
const char i18[] PROGMEM = " 19. System Info ";
const char i19[] PROGMEM = " 20. Init ";
const char i20[] PROGMEM = " 21. Exit ";
const char* const names[] PROGMEM = // Создаем массив ссылок, обращение к названию пунктов по их номеру
{
i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11,
i12, i13, i14, i15, i16, i17, i18, i19, i20
};
// Битмап с картинкой стрелочки (Если нужен)
const uint8_t ptr_bmp[] PROGMEM = {
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFF, 0xFF, 0x7E, 0x3C, 0x18,
};
//uint8_t data[ITEMS]; // Массив для параметров
uint8_t data[ITEMS] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110};
const char *SetupItems1[] = {"Setup Menu", "01. Standby", "02. Sleep",
"03. Boost", "04. Cold End Adj", "05. Tip",
"06. Stepping", "07. Password", "08. Screen Saver",
"09. Buzzer", "10. Voltage", "11. LowVol-P",
"12. Power On State", "13. Desolder", "14. Pump set",
"15. Language", "16. Date Time", "17. RTC Adjust",
"18. RTC Init", "19. System Info", "20. Init",
"21. Exit"
};
bool flagEdit = false; // Флаг редактирования параметров
//bool flagPrintMenuItems = true; // Флаг вывода строк меню
////////////// scrollpage_param_progmem ////////////////////////////
int16_t widthScreen = tft.width();
int16_t heightScreen = tft.height();
void setup() {
// set the pin modes
pinMode(SENSOR_PIN, INPUT);
pinMode(VIN_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(CONTROL_PIN, OUTPUT);
pinMode(ROTARY_1_PIN, INPUT_PULLUP);
pinMode(ROTARY_2_PIN, INPUT_PULLUP);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(SWITCH_PIN, INPUT_PULLUP);
analogWrite(CONTROL_PIN, HEATER_OFF); // this shuts off the heater
digitalWrite(BUZZER_PIN, LOW); // must be LOW when buzzer not in use
// setup ADC
ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2); // set ADC prescaler to 128
ADCSRA |= bit (ADIE); // enable ADC interrupt
interrupts (); // enable global interrupts
// setup pin change interrupt for rotary encoder
PCMSK0 = bit (PCINT0); // Configure pin change interrupt on Pin8
PCICR = bit (PCIE0); // Enable pin change interrupt
PCIFR = bit (PCIF0); // Clear interrupt flag
/*
// prepare and start OLED
if ( u8g.getMode() == U8G_MODE_R3G3B2 ) u8g.setColorIndex(255);
else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) u8g.setColorIndex(3);
else if ( u8g.getMode() == U8G_MODE_BW ) u8g.setColorIndex(1);
else if ( u8g.getMode() == U8G_MODE_HICOLOR ) u8g.setHiColorByRGB(255, 255, 255);
// get default values from EEPROM
getEEPROM();
// set screen flip
SetFlip();
// read supply voltages in mV
Vcc = getVCC(); Vin = getVIN();
// read and set current iron temperature
SetTemp = DefaultTemp;
RawTemp = denoiseAnalog(SENSOR_PIN);
ChipTemp = getChipTemp();
calculateTemp();
// turn on heater if iron temperature is well below setpoint
if ((CurrentTemp + 20) < DefaultTemp) analogWrite(CONTROL_PIN, HEATER_ON);
// set PID output range and start the PID
ctrl.SetOutputLimits(0, 255);
ctrl.SetMode(AUTOMATIC);
*/
// set initial rotary encoder values
a0 = PINB & 1; b0 = PIND >> 7 & 1; ab0 = (a0 == b0);
setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, DefaultTemp);
// reset sleep timer
sleepmillis = millis();
// long beep for setup completion
beep(); beep();
/*
////////////// menuLinearOptiPROGMEM ////////////////////////////
enc.setType(TYPE2);
enc.setFastTimeout(100);
lcd.init();
lcd.backlight();
printGUI();
////////////// menuLinearOptiPROGMEM ////////////////////////////
*/
Serial.begin(9600); // Включаем Serial
setRotary(0, ITEMS - 1, 1, 0);
////////////// scrollpage_param_progmem ////////////////////////////
enc.setTickMode(AUTO);
enc.setType(TYPE2);
enc.setFastTimeout(100);
oled.init(); // Инциализация дисплея
oled.setContrast(255); // Макс. яркость
tft.begin();
tft.fillScreen(TFT_BLACK);
tft.setRotation(1);
tft.cp437(true);
tft.setCursor(0, 0);
tft.setTextSize(2);
tft.setTextColor(TFT_MAGENTA, TFT_BLACK);
tft.print("Current State is ");
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.println("1111111");
tft.fillScreen(TFT_BLACK);
////////////// scrollpage_param_progmem ////////////////////////////
//int16_t widthScreen = tft.width(); //int16_t width(void);
//int16_t heightScreen = tft.height(); //int16_t height(void);
//Serial.println(widthScreen); Serial.println(heightScreen);
}
void loop() {
/*
ROTARYCheck(); // check rotary encoder (temp/boost setting, enter setup menu)
SLEEPCheck(); // check and activate/deactivate sleep modes
SENSORCheck(); // reads temperature and vibration switch of the iron
Thermostat(); // heater control
MainScreen(); // updates the main page on the OLED
*/
/*
////////////// menuLinearOptiPROGMEM ////////////////////////////
enc.tick();
// смена контроля
if (enc.isClick()) {
controlState = !controlState;
printGUI();
}
if (enc.isTurn()) {
int increment = 0; // локальная переменная направления
// получаем направление
if (!controlState) {
if (enc.isRight()) increment = 1;
if (enc.isLeft()) increment = -1;
arrowPos += increment; // двигаем курсор
arrowPos = constrain(arrowPos, 0, SETTINGS_AMOUNT - 1); // ограничиваем
}
increment = 0; // обнуляем инкремент
if ((controlState && enc.isRight()) || enc.isRightH()) increment = 1;
if ((controlState && enc.isLeft()) || enc.isLeftH()) increment = -1;
if (controlState && enc.isFastR()) increment = FAST_STEP;
if (controlState && enc.isFastL()) increment = -FAST_STEP;
vals[arrowPos] += increment; // меняем параметры
printGUI();
}
////////////// menuLinearOptiPROGMEM ////////////////////////////
*/
////////////// scrollpage_param_progmem ////////////////////////////
uint8_t selection = 0;
//setRotary(0, ITEMS - 1, 1, selection);
scrollpage_param_progmem (selection);
} //end of void loop()
////////////// scrollpage_param_progmem ////////////////////////////
uint8_t scrollpage_param_progmem (uint8_t selected) {
static int8_t pointer = 0; // Переменная указатель на строку меню
uint8_t editPointer = 0; // Переменная указатель на значение параметра строки меню
uint8_t lastsEditPointer = 0; // Переменная указатель на последнее значение параметра строки меню
bool flagPrintMenuItems = true; // Флаг вывода строк меню
// Header экрана меню
//widthScreen - ширина экрана
//heightScreen - высота экрана
//tft.setCursor(0, 0);
tft.setTextColor(TFT_RED, TFT_BLACK);
//tft.setCursor(50, 5);
tft.setCursor(25, 5);
tft.setTextSize(2); // Размер шрифта названия меню
/*
String Str = "MAIN MENU"; // строка для вывода в []. Состоит из выбранной строки массива OnOfItems, отцентрованной слева и справа пробелами
String Str1 = "";
int partStr; // длина пробелов вокруг строки Str
//partStr = (widthScreen - Str.length()) / 2; // длина пробелов вокруг строки
partStr = (26 - 9) / 2;
Serial.println(Str);
Serial.println(Str.length());
Serial.println(widthScreen - Str.length());
Serial.println(partStr);
for (uint8_t i = 0; i < partStr; i++) {
tft.print(" ");
}
tft.print("MAIN MENU");
for (uint8_t i = 0; i < partStr; i++) {
tft.print(" ");
}
*/
tft.print(F("MAIN MENU"));
//tft.print(Str1); Serial.println((String)Str1);
tft.fillRect(0, 26, widthScreen + 50, 2, TFT_DARKGREY);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.setTextSize(2); // Размер шрифта строк меню
//bool lastbutton = (!digitalRead(BUTTON_PIN)); // приравняем к 1 - для работы D0-While
bool repit = true;
do {
uint8_t lastselected = selected;
selected = getRotary();
// проверить нажатие кнопки поворотного энкодера
uint8_t c = digitalRead(BUTTON_PIN);
// Проверяем вращение энкодера и двигаемся по меню
if ((selected != lastselected) || (!c && c0) || flagPrintMenuItems) { //
flagPrintMenuItems = false;
pointer = selected;
pointer = constrain(pointer, 0, ITEMS - 1); // Двигаем указатель в пределах дисплея
//lastselected = selected;
/*
Serial.print("lastselected "); Serial.print(lastselected); Serial.print(" ");
Serial.print("selected "); Serial.print(selected); Serial.print(" ");
Serial.print("pointer "); Serial.print(pointer); Serial.print(" ");
Serial.print("data[] "); Serial.print(data[pointer]); Serial.println(" ");
*/
// Проверяем нажатие кнопки энкодера
c = digitalRead(BUTTON_PIN);
if (!c && c0) // Если нажата кнопка энкодера
{
beep();
buttonmillis = millis();
while ((!digitalRead(BUTTON_PIN)) && ((millis() - buttonmillis) < 500));
if (((millis() - buttonmillis) >= 500))// || enc.isHolded())
{ // Долгое нажатие кнопки энкодера
//Serial.println("Долгое нажатие кнопки энкодера!");
//flagEdit = false; // Собрасываем флаг режима редактирования
repit = false;
beep(); beep();
break;
//return; // return возвращает нас в предыдущее меню
}
else
{ // Одиночное нажатие кнопки энкодера
//Serial.println("Одиночное нажатие кнопки энкодера!");
//flagEdit = !flagEdit; // Инвертируем флаг
beep();
editParameter();
}
}
c0 = c;
//setRotary(0, ITEMS - 1, 1, selected); // вернем настройки энкодера после режима редактирования
// Выводим меню на экран
tft.setCursor(0, 40);
//tft.setTextColor(TFT_BLUE, TFT_CYAN);
// Цикл, выводящий 8 пунктов на дисплей
for (uint8_t i = 0; i < 8; i++) {
// Выводим пункты + скроллинг при достижении последней строки указателем
// Если указатель > 7, то сдвигаем список пунктов на pointer - 7
printMenuItem(pointer > 7 ? i + (pointer - 7) : i);
}
/* //Цикл считающий от значения указателя еще 8 раз
for (uint8_t i = pointer; i < pointer + 8; i++) { //
// Если результат больше ITEMS - 1 -> начинаем сначала, иначе -> продолжаем как обычно
printMenuItem(i > ITEMS - 1 ? i - ITEMS : i);
}
*/
/*
// Выводим параметры в цикле
for (uint8_t i = 0; i < 8; i++) {
//oled.setCursor(15, i); // Ставим курсор на нужную строку и позицию
//oled.print(data[pointer > 7 ? i + (pointer - 7) : i]); // Условие для вывода аналогично строкам
tft.setCursor(180, 40 + i * 16);
tft.print(data[pointer > 7 ? i + (pointer - 7) : i]);
}
*/
printPointer(constrain(pointer, 0, 7)); // Вывод указателя, в пределах 0-7 (не даем переполняться)
//oled.update(); // Выводим кадр на дисплей
////////////// scrollpage_param_progmem ////////////////////////////
//} // end if(flagPrintMenuItems)
/*
if (lastbutton && digitalRead(BUTTON_PIN))// || enc.isSingle())
//if (enc.isSingle())
{ // Если нажата кнопка энкодера
//Serial.println("Нажата кнопка!" + (String)enc.isSingle());
delay(10);
lastbutton = false; // сброси значение lastbutton в 0 - для выхода из Do - While
}
*/
}
} while (repit);//digitalRead(BUTTON_PIN) || lastbutton);// || !enc.isSingle()); // работаем, пока не нажата кнопка энкодер
tft.fillScreen(TFT_BLACK);
}
void printPointer(uint8_t pointer) {
if (flagEdit) { // Если флаг установлен
//oled.setCursor(20, pointer); // Указываем на значение параметра
//oled.print("<");
tft.setCursor(220, 40 + pointer * 16); tft.print("<");
} else { // Иначе
//oled.setCursor(0, pointer); // Указываем на параметр
//oled.print(">");
tft.setCursor(0, 40 + pointer * 16); tft.print(">");
// Можно еще в конце
//tft.setCursor(220, 40 + (pointer - 1) * 16); tft.print(" ");
//tft.setCursor(220, 40 + pointer * 16); tft.print("<");
////FrameSelection (0, 39+pointer * 16, 200, 18, TFT_GREEN);
// Указатель в виде прямоугольной рамки со скругленными углами
//tft.drawRoundRect(0, 39+pointer * 16, 220, 18, 6, TFT_GREEN);
// Указатель в виде битмапа (картинки)
//oled.drawBitmap(0, 40+pointer * 8, ptr_bmp, 10, 8);
//ft.drawBitmap(0, 40+pointer * 8, ptr_bmp, 10, 8);
}
}
// Функция для печати строки из prm
void printMenuItem(uint8_t num) {
char buffer[26]; // Буфер на полную строку
uint16_t ptr = pgm_read_word(&(names[num])); // Получаем указатель на первый символ строки
uint8_t i = 0; // Переменная - счетчик
do { // Начало цикла
buffer[i] = (char)(pgm_read_byte(ptr++)); // Прочитать в буфер один символ из PGM и подвинуть указатель на 1
} while (buffer[i++] != NULL); // Если это не конец строки - вернуться в начало цикла
// Вывод готовой строки с переносом на следующую
//tft.print(buffer); tft.println(" ");
/*
char bufferItemName[26];
strcpy(bufferItemName, buffer);
for (int i = strlen(buffer); i < 26; i++) {
bufferItemName[i] = ' '; // заполнение пробелами
}
bufferItemName[26] = '\0';
tft.println(bufferItemName);
*/
tft.println(buffer);
}
////////////// scrollpage_param_progmem ////////////////////////////
void editParameter() {
Serial.println("editParameter()");
/*
// Если флаг редактирования установлен
//flagEdit=false;
//pointer = constrain(pointer, 0, ITEMS - 1);
//uint8_t tempPrintPointer = pointer;
////uint8_t lastsEditPointer = editPointer;
setRotary(0, 255, 1, data[pointer]); // Перегружаем энкодер для измененя значений параметра
//lastsEditPointer = editPointer;
editPointer = getRotary(); // + countStep; // Опрашиваем энкодер для получения значений параметра
Serial.print("До "); Serial.print(lastsEditPointer); Serial.print(" "); Serial.print(editPointer); Serial.print(" "); Serial.println(count);
if (editPointer != lastsEditPointer) { // Если ручку энкодера вращали
editPointer = constrain(editPointer, 0, 255); // Изменяем значение параметра в заданных пределах
data[pointer] = editPointer; // Изменяем параметр новым значением
Serial.print("После "); Serial.print(lastsEditPointer); Serial.print(" "); Serial.print(editPointer); Serial.print(" "); Serial.println(count);
}
lastsEditPointer = editPointer; // Присваиваем переменной текущие значения параметра
setRotary(0, ITEMS - 1, 1, pointer); // Перегружаем энкодер для перемещения по строкам меню
selected = getRotary(); // // Опрашиваем энкодер
pointer = selected; lastselected = selected; // для получения указателя на текущую строку меню
//if (selected > lastselected) {
//data[pointer]++; // Увеличиваем параметр на 1
//} else if (selected < lastselected) {
// data[pointer]--; // Уменьшаем параметр на 1
//data[pointer]--; // Уменьшаем параметр на 1
//}
*/
}
/*
////////////// menuLinearOptiPROGMEM ////////////////////////////
void printGUI() {
static int8_t screenPos = 0; // номер "экрана"
static int8_t lastScreen = 0; // предыдущий номер "экрана"
screenPos = arrowPos / LINES; // ищем номер экрана (0..3 - 0, 4..7 - 1)
if (lastScreen != screenPos) lcd.clear(); // если экран сменился - очищаем
lastScreen = screenPos;
for (byte i = 0; i < LINES; i++) { // для всех строк
lcd.setCursor(0, i); // курсор в начало
u8g.
// если курсор находится на выбранной строке
smartArrow(arrowPos == LINES * screenPos + i); // рисуем стрелку или пробел
// если пункты меню закончились, покидаем цикл for
if (LINES * screenPos + i == SETTINGS_AMOUNT) break;
// выводим имя и значение пункта меню
printFromPGM(&names[LINES * screenPos + i]);
lcd.print(F(": "));
lcd.print(vals[LINES * screenPos + i]);
lcd.print(F(" ")); // пробелы для очистки
}
}
// очень хитрая функция для печати из PROGMEM
void printFromPGM(int charMap) {
uint16_t ptr = pgm_read_word(charMap); // получаем адрес из таблицы ссылок
while (pgm_read_byte(ptr) != NULL) { // всю строку до нулевого символа
lcd.print(char(pgm_read_byte(ptr))); // выводим в монитор или куда нам надо
ptr++; // следующий символ
}
}
void smartArrow(bool state) { // рисует стрелку, галку или пробел
lcd.write(state ? (controlState ? 62 : 126) : 32);
}
////////////// menuLinearOptiPROGMEM ////////////////////////////
*/
/*
// check rotary encoder; set temperature, toggle boost mode, enter setup menu accordingly
void ROTARYCheck() {
// set working temperature according to rotary encoder value
SetTemp = getRotary();
// check rotary encoder switch
uint8_t c = digitalRead(BUTTON_PIN);
if ( !c && c0 ) {
beep();
buttonmillis = millis();
while ( (!digitalRead(BUTTON_PIN)) && ((millis() - buttonmillis) < 500) );
if ((millis() - buttonmillis) >= 500) SetupScreen();
else {
inBoostMode = !inBoostMode;
if (inBoostMode) boostmillis = millis();
handleMoved = true;
}
}
c0 = c;
// check timer when in boost mode
if (inBoostMode && timeOfBoost) {
goneSeconds = (millis() - boostmillis) / 1000;
if (goneSeconds >= timeOfBoost) {
inBoostMode = false; // stop boost mode
beep(); // beep if boost mode is over
beepIfWorky = true; // beep again when working temperature is reached
}
}
}
// check and activate/deactivate sleep modes
void SLEEPCheck() {
if (handleMoved) { // if handle was moved
if (inSleepMode) { // in sleep or off mode?
if ((CurrentTemp + 20) < SetTemp) // if temp is well below setpoint
analogWrite(CONTROL_PIN, HEATER_ON); // then start the heater right now
beep(); // beep on wake-up
beepIfWorky = true; // beep again when working temperature is reached
}
handleMoved = false; // reset handleMoved flag
inSleepMode = false; // reset sleep flag
inOffMode = false; // reset off flag
sleepmillis = millis(); // reset sleep timer
}
// check time passed since the handle was moved
goneMinutes = (millis() - sleepmillis) / 60000;
if ( (!inSleepMode) && (time2sleep > 0) && (goneMinutes >= time2sleep) ) {
inSleepMode = true;
beep();
}
if ( (!inOffMode) && (time2off > 0) && (goneMinutes >= time2off ) ) {
inOffMode = true;
beep();
}
}
// reads temperature, vibration switch and supply voltages
void SENSORCheck() {
analogWrite(CONTROL_PIN, HEATER_OFF); // shut off heater in order to measure temperature
delayMicroseconds(TIME2SETTLE); // wait for voltage to settle
double temp = denoiseAnalog(SENSOR_PIN); // read ADC value for temperature
uint8_t d = digitalRead(SWITCH_PIN); // check handle vibration switch
if (d != d0) {
handleMoved = true; // set flag if handle was moved
d0 = d;
}
if (! SensorCounter--) Vin = getVIN(); // get Vin every now and then
analogWrite(CONTROL_PIN, HEATER_PWM); // turn on again heater
RawTemp += (temp - RawTemp) * SMOOTHIE; // stabilize ADC temperature reading
calculateTemp(); // calculate real temperature value
// stabilize displayed temperature when around setpoint
if ((ShowTemp != Setpoint) || (abs(ShowTemp - CurrentTemp) > 5)) ShowTemp = CurrentTemp;
if (abs(ShowTemp - Setpoint) <= 1) ShowTemp = Setpoint;
// set state variable if temperature is in working range; beep if working temperature was just reached
gap = abs(SetTemp - CurrentTemp);
if (gap < 5) {
if (!isWorky && beepIfWorky) beep();
isWorky = true;
beepIfWorky = false;
}
else isWorky = false;
// checks if tip is present or currently inserted
if (ShowTemp > 500) TipIsPresent = false; // tip removed ?
if (!TipIsPresent && (ShowTemp < 500)) { // new tip inserted ?
analogWrite(CONTROL_PIN, HEATER_OFF); // shut off heater
beep(); // beep for info
TipIsPresent = true; // tip is present now
ChangeTipScreen(); // show tip selection screen
updateEEPROM(); // update setting in EEPROM
handleMoved = true; // reset all timers
RawTemp = denoiseAnalog(SENSOR_PIN); // restart temp smooth algorithm
c0 = LOW; // switch must be released
setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, SetTemp); // reset rotary encoder
}
}
// calculates real temperature value according to ADC reading and calibration values
void calculateTemp() {
if (RawTemp < 200) CurrentTemp = map (RawTemp, 0, 200, 21, CalTemp[CurrentTip][0]);
else if (RawTemp < 280) CurrentTemp = map (RawTemp, 200, 280, CalTemp[CurrentTip][0], CalTemp[CurrentTip][1]);
else CurrentTemp = map (RawTemp, 280, 360, CalTemp[CurrentTip][1], CalTemp[CurrentTip][2]);
}
// controls the heater
void Thermostat() {
// define Setpoint acoording to current working mode
if (inOffMode) Setpoint = 0;
else if (inSleepMode) Setpoint = SleepTemp;
else if (inBoostMode) Setpoint = SetTemp + BoostTemp;
else Setpoint = SetTemp;
// control the heater (PID or direct)
gap = abs(Setpoint - CurrentTemp);
if (PIDenable) {
Input = CurrentTemp;
if (gap < 30) ctrl.SetTunings(consKp, consKi, consKd);
else ctrl.SetTunings(aggKp, aggKi, aggKd);
ctrl.Compute();
} else {
// turn on heater if current temperature is below setpoint
if ((CurrentTemp + 0.5) < Setpoint) Output = 0; else Output = 255;
}
analogWrite(CONTROL_PIN, HEATER_PWM); // set heater PWM
}
*/
// creates a short beep on the buzzer
void beep() {
if (beepEnable) {
for (uint8_t i = 0; i < 255; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delayMicroseconds(125);
digitalWrite(BUZZER_PIN, LOW);
delayMicroseconds(125);
}
}
}
// sets start values for rotary encoder
void setRotary(int rmin, int rmax, int rstep, int rvalue) {
countMin = rmin << ROTARY_TYPE;
countMax = rmax << ROTARY_TYPE;
countStep = ECReverse ? -rstep : rstep;
count = rvalue << ROTARY_TYPE;
}
// reads current rotary encoder value
int getRotary() {
return (count >> ROTARY_TYPE);
}
/*
// reads user settings from EEPROM; if EEPROM values are invalid, write defaults
void getEEPROM() {
uint16_t identifier = (EEPROM.read(0) << 8) | EEPROM.read(1);
if (identifier == EEPROM_IDENT) {
DefaultTemp = (EEPROM.read(2) << 8) | EEPROM.read(3);
SleepTemp = (EEPROM.read(4) << 8) | EEPROM.read(5);
BoostTemp = EEPROM.read(6);
time2sleep = EEPROM.read(7);
time2off = EEPROM.read(8);
timeOfBoost = EEPROM.read(9);
MainScrType = EEPROM.read(10);
PIDenable = EEPROM.read(11);
beepEnable = EEPROM.read(12);
BodyFlip = EEPROM.read(13);
ECReverse = EEPROM.read(14);
CurrentTip = EEPROM.read(15);
NumberOfTips = EEPROM.read(16);
uint8_t i, j;
uint16_t counter = 17;
for (i = 0; i < NumberOfTips; i++) {
for (j = 0; j < TIPNAMELENGTH; j++) {
TipName[i][j] = EEPROM.read(counter++);
}
for (j = 0; j < 4; j++) {
CalTemp[i][j] = EEPROM.read(counter++) << 8;
CalTemp[i][j] |= EEPROM.read(counter++);
}
}
}
else {
EEPROM.update(0, EEPROM_IDENT >> 8); EEPROM.update(1, EEPROM_IDENT & 0xFF);
updateEEPROM();
}
}
// writes user settings to EEPROM using updade function to minimize write cycles
void updateEEPROM() {
EEPROM.update( 2, DefaultTemp >> 8);
EEPROM.update( 3, DefaultTemp & 0xFF);
EEPROM.update( 4, SleepTemp >> 8);
EEPROM.update( 5, SleepTemp & 0xFF);
EEPROM.update( 6, BoostTemp);
EEPROM.update( 7, time2sleep);
EEPROM.update( 8, time2off);
EEPROM.update( 9, timeOfBoost);
EEPROM.update(10, MainScrType);
EEPROM.update(11, PIDenable);
EEPROM.update(12, beepEnable);
EEPROM.update(13, BodyFlip);
EEPROM.update(14, ECReverse);
EEPROM.update(15, CurrentTip);
EEPROM.update(16, NumberOfTips);
uint8_t i, j;
uint16_t counter = 17;
for (i = 0; i < NumberOfTips; i++) {
for (j = 0; j < TIPNAMELENGTH; j++) EEPROM.update(counter++, TipName[i][j]);
for (j = 0; j < 4; j++) {
EEPROM.update(counter++, CalTemp[i][j] >> 8);
EEPROM.update(counter++, CalTemp[i][j] & 0xFF);
}
}
}
// check state and flip screen
void SetFlip() {
if (BodyFlip) u8g.setRot180();
else u8g.undoRotation();
}
// draws the main screen
void MainScreen() {
u8g.firstPage();
do {
// draw setpoint temperature
u8g.setFont(u8g_font_9x15);
u8g.setFontPosTop();
u8g.drawStr( 0, 0, "SET:");
u8g.setPrintPos(40, 0);
u8g.print(Setpoint, 0);
// draw status of heater
u8g.setPrintPos(83, 0);
if (ShowTemp > 500) u8g.print(F("ERROR"));
else if (inOffMode) u8g.print(F(" OFF"));
else if (inSleepMode) u8g.print(F("SLEEP"));
else if (inBoostMode) u8g.print(F("BOOST"));
else if (isWorky) u8g.print(F("WORKY"));
else if (Output < 180) u8g.print(F(" HEAT"));
else u8g.print(F(" HOLD"));
// rest depending on main screen type
if (MainScrType) {
// draw current tip and input voltage
float fVin = (float)Vin / 1000; // convert mv in V
u8g.setPrintPos( 0, 52); u8g.print(TipName[CurrentTip]);
u8g.setPrintPos(83, 52); u8g.print(fVin, 1); u8g.print(F("V"));
// draw current temperature
u8g.setFont(u8g_font_freedoomr25n);
u8g.setFontPosTop();
u8g.setPrintPos(37, 22);
if (ShowTemp > 500) u8g.print(F("000")); else u8g.print(ShowTemp);
} else {
// draw current temperature in big figures
u8g.setFont(u8g_font_fub42n);
u8g.setFontPosTop();
u8g.setPrintPos(15, 20);
if (ShowTemp > 500) u8g.print(F("000")); else u8g.print(ShowTemp);
}
} while (u8g.nextPage());
}
// setup screen
void SetupScreen() {
analogWrite(CONTROL_PIN, HEATER_OFF); // shut off heater
beep();
uint16_t SaveSetTemp = SetTemp;
uint8_t selection = 0;
bool repeat = true;
while (repeat) {
selection = MenuScreen(SetupItems, sizeof(SetupItems), selection);
switch (selection) {
case 0: TipScreen(); repeat = false; break;
case 1: TempScreen(); break;
case 2: TimerScreen(); break;
case 3: PIDenable = MenuScreen(ControlTypeItems, sizeof(ControlTypeItems), PIDenable); break;
case 4: MainScrType = MenuScreen(MainScreenItems, sizeof(MainScreenItems), MainScrType); break;
case 5: beepEnable = MenuScreen(BuzzerItems, sizeof(BuzzerItems), beepEnable); break;
case 6: BodyFlip = MenuScreen(FlipItems, sizeof(FlipItems), BodyFlip); SetFlip(); break;
case 7: ECReverse = MenuScreen(ECReverseItems, sizeof(ECReverseItems), ECReverse); break;
case 8: InfoScreen(); break;
default: repeat = false; break;
}
}
updateEEPROM();
handleMoved = true;
SetTemp = SaveSetTemp;
setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, SetTemp);
}
// tip settings screen
void TipScreen() {
uint8_t selection = 0;
bool repeat = true;
while (repeat) {
selection = MenuScreen(TipItems, sizeof(TipItems), selection);
switch (selection) {
case 0: ChangeTipScreen(); break;
case 1: CalibrationScreen(); break;
case 2: InputNameScreen(); break;
case 3: DeleteTipScreen(); break;
case 4: AddTipScreen(); break;
default: repeat = false; break;
}
}
}
// temperature settings screen
void TempScreen() {
uint8_t selection = 0;
bool repeat = true;
while (repeat) {
selection = MenuScreen(TempItems, sizeof(TempItems), selection);
switch (selection) {
case 0: setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, DefaultTemp);
DefaultTemp = InputScreen(DefaultTempItems); break;
case 1: setRotary(20, 200, TEMP_STEP, SleepTemp);
SleepTemp = InputScreen(SleepTempItems); break;
case 2: setRotary(10, 100, TEMP_STEP, BoostTemp);
BoostTemp = InputScreen(BoostTempItems); break;
default: repeat = false; break;
}
}
}
// timer settings screen
void TimerScreen() {
uint8_t selection = 0;
bool repeat = true;
while (repeat) {
selection = MenuScreen(TimerItems, sizeof(TimerItems), selection);
switch (selection) {
case 0: setRotary(0, 30, 1, time2sleep);
time2sleep = InputScreen(SleepTimerItems); break;
case 1: setRotary(0, 60, 5, time2off);
time2off = InputScreen(OffTimerItems); break;
case 2: setRotary(0, 180, 10, timeOfBoost);
timeOfBoost = InputScreen(BoostTimerItems); break;
default: repeat = false; break;
}
}
}
// menu screen
uint8_t MenuScreen(const char *Items[], uint8_t numberOfItems, uint8_t selected) {
bool isTipScreen = (Items[0] == "Tip:");
uint8_t lastselected = selected;
int8_t arrow = 0;
if (selected) arrow = 1;
numberOfItems >>= 1;
setRotary(0, numberOfItems - 2, 1, selected);
bool lastbutton = (!digitalRead(BUTTON_PIN));
do {
selected = getRotary();
arrow = constrain(arrow + selected - lastselected, 0, 2);
lastselected = selected;
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setFontPosTop();
u8g.drawStr( 0, 0, Items[0]);
if (isTipScreen) u8g.drawStr( 54, 0, TipName[CurrentTip]);
u8g.drawStr( 0, 16 * (arrow + 1), ">");
for (uint8_t i = 0; i < 3; i++) {
uint8_t drawnumber = selected + i + 1 - arrow;
if (drawnumber < numberOfItems)
u8g.drawStr( 12, 16 * (i + 1), Items[selected + i + 1 - arrow]);
}
} while (u8g.nextPage());
if (lastbutton && digitalRead(BUTTON_PIN)) {
delay(10);
lastbutton = false;
}
} while (digitalRead(BUTTON_PIN) || lastbutton);
beep();
return selected;
}
void MessageScreen(const char *Items[], uint8_t numberOfItems) {
bool lastbutton = (!digitalRead(BUTTON_PIN));
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setFontPosTop();
for (uint8_t i = 0; i < numberOfItems; i++) u8g.drawStr( 0, i * 16, Items[i]);
} while (u8g.nextPage());
do {
if (lastbutton && digitalRead(BUTTON_PIN)) {
delay(10);
lastbutton = false;
}
} while (digitalRead(BUTTON_PIN) || lastbutton);
beep();
}
// input value screen
uint16_t InputScreen(const char *Items[]) {
uint16_t value;
bool lastbutton = (!digitalRead(BUTTON_PIN));
do {
value = getRotary();
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setFontPosTop();
u8g.drawStr( 0, 0, Items[0]);
u8g.setPrintPos(0, 32); u8g.print(">"); u8g.setPrintPos(10, 32);
if (value == 0) u8g.print(F("Deactivated"));
else {
u8g.print(value);
u8g.print(" ");
u8g.print(Items[1]);
}
} while (u8g.nextPage());
if (lastbutton && digitalRead(BUTTON_PIN)) {
delay(10);
lastbutton = false;
}
} while (digitalRead(BUTTON_PIN) || lastbutton);
beep();
return value;
}
// information display screen
void InfoScreen() {
bool lastbutton = (!digitalRead(BUTTON_PIN));
do {
Vcc = getVCC(); // read input voltage
float fVcc = (float)Vcc / 1000; // convert mV in V
Vin = getVIN(); // read supply voltage
float fVin = (float)Vin / 1000; // convert mv in V
float fTmp = getChipTemp(); // read cold junction temperature
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setFontPosTop();
u8g.setPrintPos(0, 0); u8g.print(F("Firmware: ")); u8g.print(VERSION);
u8g.setPrintPos(0, 16); u8g.print(F("Tmp: ")); u8g.print(fTmp, 1); u8g.print(F(" C"));
u8g.setPrintPos(0, 32); u8g.print(F("Vin: ")); u8g.print(fVin, 1); u8g.print(F(" V"));
u8g.setPrintPos(0, 48); u8g.print(F("Vcc: ")); u8g.print(fVcc, 1); u8g.print(F(" V"));
} while (u8g.nextPage());
if (lastbutton && digitalRead(BUTTON_PIN)) {
delay(10);
lastbutton = false;
}
} while (digitalRead(BUTTON_PIN) || lastbutton);
beep();
}
// change tip screen
void ChangeTipScreen() {
uint8_t selected = CurrentTip;
uint8_t lastselected = selected;
int8_t arrow = 0;
if (selected) arrow = 1;
setRotary(0, NumberOfTips - 1, 1, selected);
bool lastbutton = (!digitalRead(BUTTON_PIN));
do {
selected = getRotary();
arrow = constrain(arrow + selected - lastselected, 0, 2);
lastselected = selected;
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setFontPosTop();
u8g.drawStr( 0, 0, F("Select Tip"));
u8g.drawStr( 0, 16 * (arrow + 1), ">");
for (uint8_t i = 0; i < 3; i++) {
uint8_t drawnumber = selected + i - arrow;
if (drawnumber < NumberOfTips)
u8g.drawStr( 12, 16 * (i + 1), TipName[selected + i - arrow]);
}
} while (u8g.nextPage());
if (lastbutton && digitalRead(BUTTON_PIN)) {
delay(10);
lastbutton = false;
}
} while (digitalRead(BUTTON_PIN) || lastbutton);
beep();
CurrentTip = selected;
}
// temperature calibration screen
void CalibrationScreen() {
uint16_t CalTempNew[4];
for (uint8_t CalStep = 0; CalStep < 3; CalStep++) {
SetTemp = CalTemp[CurrentTip][CalStep];
setRotary(100, 500, 1, SetTemp);
beepIfWorky = true;
bool lastbutton = (!digitalRead(BUTTON_PIN));
do {
SENSORCheck(); // reads temperature and vibration switch of the iron
Thermostat(); // heater control
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setFontPosTop();
u8g.drawStr( 0, 0, F("Calibration"));
u8g.setPrintPos(0, 16); u8g.print(F("Step: ")); u8g.print(CalStep + 1); u8g.print(" of 3");
if (isWorky) {
u8g.setPrintPos(0, 32); u8g.print(F("Set measured"));
u8g.setPrintPos(0, 48); u8g.print(F("temp: ")); u8g.print(getRotary());
} else {
u8g.setPrintPos(0, 32); u8g.print(F("ADC: ")); u8g.print(uint16_t(RawTemp));
u8g.setPrintPos(0, 48); u8g.print(F("Please wait..."));
}
} while (u8g.nextPage());
if (lastbutton && digitalRead(BUTTON_PIN)) {
delay(10);
lastbutton = false;
}
} while (digitalRead(BUTTON_PIN) || lastbutton);
CalTempNew[CalStep] = getRotary();
beep(); delay (10);
}
analogWrite(CONTROL_PIN, HEATER_OFF); // shut off heater
delayMicroseconds(TIME2SETTLE); // wait for voltage to settle
CalTempNew[3] = getChipTemp(); // read chip temperature
if ((CalTempNew[0] + 10 < CalTempNew[1]) && (CalTempNew[1] + 10 < CalTempNew[2])) {
if (MenuScreen(StoreItems, sizeof(StoreItems), 0)) {
for (uint8_t i = 0; i < 4; i++) CalTemp[CurrentTip][i] = CalTempNew[i];
}
}
}
// input tip name screen
void InputNameScreen() {
uint8_t value;
for (uint8_t digit = 0; digit < (TIPNAMELENGTH - 1); digit++) {
bool lastbutton = (!digitalRead(BUTTON_PIN));
setRotary(31, 96, 1, 65);
do {
value = getRotary();
if (value == 31) {
value = 95;
setRotary(31, 96, 1, 95);
}
if (value == 96) {
value = 32;
setRotary(31, 96, 1, 32);
}
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setFontPosTop();
u8g.drawStr( 0, 0, F("Enter Tip Name"));
u8g.setPrintPos(9 * digit, 48); u8g.print(char(94));
u8g.setPrintPos(0, 32);
for (uint8_t i = 0; i < digit; i++) u8g.print(TipName[CurrentTip][i]);
u8g.setPrintPos(9 * digit, 32); u8g.print(char(value));
} while (u8g.nextPage());
if (lastbutton && digitalRead(BUTTON_PIN)) {
delay(10);
lastbutton = false;
}
} while (digitalRead(BUTTON_PIN) || lastbutton);
TipName[CurrentTip][digit] = value;
beep(); delay (10);
}
TipName[CurrentTip][TIPNAMELENGTH - 1] = 0;
return;
}
// delete tip screen
void DeleteTipScreen() {
if (NumberOfTips == 1) {
MessageScreen(DeleteMessage, sizeof(DeleteMessage));
}
else if (MenuScreen(SureItems, sizeof(SureItems), 0)) {
if (CurrentTip == (NumberOfTips - 1)) {
CurrentTip--;
}
else {
for (uint8_t i = CurrentTip; i < (NumberOfTips - 1); i++) {
for (uint8_t j = 0; j < TIPNAMELENGTH; j++) TipName[i][j] = TipName[i + 1][j];
for (uint8_t j = 0; j < 4; j++) CalTemp[i][j] = CalTemp[i + 1][j];
}
}
NumberOfTips--;
}
}
// add new tip screen
void AddTipScreen() {
if (NumberOfTips < TIPMAX) {
CurrentTip = NumberOfTips++; InputNameScreen();
CalTemp[CurrentTip][0] = TEMP200; CalTemp[CurrentTip][1] = TEMP280;
CalTemp[CurrentTip][2] = TEMP360; CalTemp[CurrentTip][3] = TEMPCHP;
} else MessageScreen(MaxTipMessage, sizeof(MaxTipMessage));
}
// average several ADC readings in sleep mode to denoise
uint16_t denoiseAnalog (byte port) {
uint16_t result = 0;
ADCSRA |= bit (ADEN) | bit (ADIF); // enable ADC, turn off any pending interrupt
if (port >= A0) port -= A0; // set port and
ADMUX = (0x0F & port) | bit(REFS0); // reference to AVcc
set_sleep_mode (SLEEP_MODE_ADC); // sleep during sample for noise reduction
for (uint8_t i = 0; i < 32; i++) { // get 32 readings
sleep_mode(); // go to sleep while taking ADC sample
while (bitRead(ADCSRA, ADSC)); // make sure sampling is completed
result += ADC; // add them up
}
bitClear (ADCSRA, ADEN); // disable ADC
return (result >> 5); // devide by 32 and return value
}
// get internal temperature by reading ADC channel 8 against 1.1V reference
double getChipTemp() {
uint16_t result = 0;
ADCSRA |= bit (ADEN) | bit (ADIF); // enable ADC, turn off any pending interrupt
ADMUX = bit (REFS1) | bit (REFS0) | bit (MUX3); // set reference and mux
delay(20); // wait for voltages to settle
set_sleep_mode (SLEEP_MODE_ADC); // sleep during sample for noise reduction
for (uint8_t i = 0; i < 32; i++) { // get 32 readings
sleep_mode(); // go to sleep while taking ADC sample
while (bitRead(ADCSRA, ADSC)); // make sure sampling is completed
result += ADC; // add them up
}
bitClear (ADCSRA, ADEN); // disable ADC
result >>= 2; // devide by 4
return ((result - 2594) / 9.76); // calculate internal temperature in degrees C
}
// get input voltage in mV by reading 1.1V reference against AVcc
uint16_t getVCC() {
uint16_t result = 0;
ADCSRA |= bit (ADEN) | bit (ADIF); // enable ADC, turn off any pending interrupt
// set Vcc measurement against 1.1V reference
ADMUX = bit (REFS0) | bit (MUX3) | bit (MUX2) | bit (MUX1);
delay(1); // wait for voltages to settle
set_sleep_mode (SLEEP_MODE_ADC); // sleep during sample for noise reduction
for (uint8_t i = 0; i < 16; i++) { // get 16 readings
sleep_mode(); // go to sleep while taking ADC sample
while (bitRead(ADCSRA, ADSC)); // make sure sampling is completed
result += ADC; // add them up
}
bitClear (ADCSRA, ADEN); // disable ADC
result >>= 4; // devide by 16
return (1125300L / result); // 1125300 = 1.1 * 1023 * 1000
}
// get supply voltage in mV
uint16_t getVIN() {
long result;
result = denoiseAnalog (VIN_PIN); // read supply voltage via voltage divider
return (result * Vcc / 179.474); // 179.474 = 1023 * R13 / (R12 + R13)
}
// ADC interrupt service routine
EMPTY_INTERRUPT (ADC_vect); // nothing to be done here
*/
// Pin change interrupt service routine for rotary encoder
ISR (PCINT0_vect) {
uint8_t a = PINB & 1;
uint8_t b = PIND >> 7 & 1;
if (a != a0) { // A changed
a0 = a;
if (b != b0) { // B changed
b0 = b;
count = constrain(count + ((a == b) ? countStep : -countStep), countMin, countMax);
if (ROTARY_TYPE && ((a == b) != ab0)) {
count = constrain(count + ((a == b) ? countStep : -countStep), countMin, countMax);;
}
ab0 = (a == b);
handleMoved = true;
}
}
}
void FrameSelection (uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t selectcolor) {
tft.drawRect(x0, y0, w, h, selectcolor); // делаем жирный квадрат из 5 квадратов вложенных друг в друга
tft.drawRect(x0 + 1, y0 + 1, w - 2, h - 2, selectcolor); // делаем жирный квадрат из 5 квадратов вложенных друг в друга
//tft.drawRect(x0 + 2, y0 + 2, w - 4, h - 4, selectcolor); // делаем жирный квадрат из 5 квадратов вложенных друг в друга
//tft.drawRect(x0 + 3, y0 + 3, w - 6, h - 6, selectcolor); // делаем жирный квадрат из 5 квадратов вложенных друг в друга
//tft.drawRect(x0 + 4, y0 + 4, w - 8, h - 8, selectcolor); // делаем жирный квадрат из 5 квадратов вложенных друг в друга
}