From 9a71d393340bfc928f147831aa606113cf76076e Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Tue, 9 Apr 2024 16:43:14 +0900 Subject: [PATCH 1/6] removed old code related to menu/button controls A major redesign of this project. - removed buttons related blocking code - removed all menu and navigation related code - for buttons control now using ESPAsyncButton lib - refactored text constants and multiling dictionary scheme - Implement IronHID class that will work with button coltrols - Implement IronScreen class to work with display - display updates will be on-demand based on event messages most of the menu functions does not work for now. --- SolderingPen_ESP32S2/UtilsEEPROM.cpp | 27 +- SolderingPen_ESP32S2/UtilsEEPROM.h | 16 +- SolderingPen_ESP32S2/common.hpp | 31 + SolderingPen_ESP32S2/config.h | 29 +- SolderingPen_ESP32S2/const.h | 14 + SolderingPen_ESP32S2/evtloop.cpp | 4 +- SolderingPen_ESP32S2/evtloop.hpp | 21 +- SolderingPen_ESP32S2/heater.cpp | 249 ++++-- SolderingPen_ESP32S2/heater.hpp | 77 +- SolderingPen_ESP32S2/hid.cpp | 394 +++++++++ SolderingPen_ESP32S2/hid.hpp | 160 ++++ SolderingPen_ESP32S2/ironcontroller.cpp | 239 ++++++ SolderingPen_ESP32S2/ironcontroller.hpp | 109 +++ SolderingPen_ESP32S2/lang/dictionaries.h | 26 + SolderingPen_ESP32S2/lang/i18n.h | 36 + SolderingPen_ESP32S2/lang/lang_en_us.h | 39 + SolderingPen_ESP32S2/lang/lang_ru_ru.h | 38 + SolderingPen_ESP32S2/lang/lang_zh_cn.h | 37 + SolderingPen_ESP32S2/lang/lang_zh_tw.h | 35 + SolderingPen_ESP32S2/log.h | 35 +- SolderingPen_ESP32S2/main.cpp | 992 +---------------------- SolderingPen_ESP32S2/nvs.cpp | 49 ++ SolderingPen_ESP32S2/nvs.hpp | 21 + SolderingPen_ESP32S2/oldcode.cpp | 736 +++++++++++++++++ SolderingPen_ESP32S2/sensors.cpp | 122 ++- SolderingPen_ESP32S2/sensors.hpp | 46 +- 26 files changed, 2501 insertions(+), 1081 deletions(-) create mode 100644 SolderingPen_ESP32S2/common.hpp create mode 100644 SolderingPen_ESP32S2/hid.cpp create mode 100644 SolderingPen_ESP32S2/hid.hpp create mode 100644 SolderingPen_ESP32S2/ironcontroller.cpp create mode 100644 SolderingPen_ESP32S2/ironcontroller.hpp create mode 100644 SolderingPen_ESP32S2/lang/dictionaries.h create mode 100644 SolderingPen_ESP32S2/lang/i18n.h create mode 100644 SolderingPen_ESP32S2/lang/lang_en_us.h create mode 100644 SolderingPen_ESP32S2/lang/lang_ru_ru.h create mode 100644 SolderingPen_ESP32S2/lang/lang_zh_cn.h create mode 100644 SolderingPen_ESP32S2/lang/lang_zh_tw.h create mode 100644 SolderingPen_ESP32S2/nvs.cpp create mode 100644 SolderingPen_ESP32S2/nvs.hpp create mode 100644 SolderingPen_ESP32S2/oldcode.cpp diff --git a/SolderingPen_ESP32S2/UtilsEEPROM.cpp b/SolderingPen_ESP32S2/UtilsEEPROM.cpp index 7c94fae..46fdac8 100644 --- a/SolderingPen_ESP32S2/UtilsEEPROM.cpp +++ b/SolderingPen_ESP32S2/UtilsEEPROM.cpp @@ -2,13 +2,14 @@ bool write_default_EEPROM(){ Serial.println("Writing default config to EEPROM"); - +/* EEPROM.writeUShort(ADDR_DEFAULT_TEMP, TEMP_DEFAULT); EEPROM.writeUShort(ADDR_SLEEP_TEMP, TEMP_SLEEP); EEPROM.writeUChar(ADDR_BOOST_TEMP, TEMP_BOOST); EEPROM.writeUShort(ADDR_TIME_2_SLEEP, TIME2SLEEP); EEPROM.writeUChar(ADDR_TIME_2_OFF, TIME2OFF); EEPROM.writeUChar(ADDR_TIME_OF_BOOST, TIMEOFBOOST); +*/ EEPROM.writeUChar(ADDR_MAIN_SCREEN, MAINSCREEN); // EEPROM.writeBool(ADDR_PID_ENABLE, PID_ENABLE); EEPROM.writeBool(ADDR_BEEP_ENABLE, BEEP_ENABLE); @@ -36,7 +37,7 @@ bool write_default_EEPROM(){ EEPROM.writeUChar(ADDR_LANGUAGE, DEFAULT_LANGUAGE); EEPROM.writeUChar(ADDR_HAND_SIDE, DEFAULT_HAND_SIDE); - EEPROM.writeUInt(ADDR_SYSTEM_INIT_FLAG, VERSION_NUM); + EEPROM.writeUInt(ADDR_SYSTEM_INIT_FLAG, FW_VERSION_NUM); if (EEPROM.commit()) { @@ -66,19 +67,20 @@ bool init_EEPROM(){ bool update_EEPROM(){ Serial.println("Updating EEPROM"); - +/* EEPROM.writeUShort(ADDR_DEFAULT_TEMP, DefaultTemp); EEPROM.writeUShort(ADDR_SLEEP_TEMP, SleepTemp); EEPROM.writeUChar(ADDR_BOOST_TEMP, BoostTemp); EEPROM.writeUShort(ADDR_TIME_2_SLEEP, time2sleep); EEPROM.writeUChar(ADDR_TIME_2_OFF, time2off); EEPROM.writeUChar(ADDR_TIME_OF_BOOST, timeOfBoost); +*/ EEPROM.writeUChar(ADDR_MAIN_SCREEN, MainScrType); // EEPROM.writeBool(ADDR_PID_ENABLE, PIDenable); EEPROM.writeBool(ADDR_BEEP_ENABLE, beepEnable); - EEPROM.writeUChar(ADDR_VOLTAGE_VALUE, VoltageValue); +// EEPROM.writeUChar(ADDR_VOLTAGE_VALUE, VoltageValue); EEPROM.writeBool(ADDR_QC_ENABLE, QCEnable); - EEPROM.writeUChar(ADDR_WAKEUP_THRESHOLD, WAKEUPthreshold); + //EEPROM.writeUChar(ADDR_WAKEUP_THRESHOLD, WAKEUPthreshold); EEPROM.writeUChar(ADDR_CURRENT_TIP, CurrentTip); EEPROM.writeUChar(ADDR_NUMBER_OF_TIPS, NumberOfTips); @@ -94,7 +96,7 @@ bool update_EEPROM(){ EEPROM.writeUChar(ADDR_LANGUAGE, language); EEPROM.writeUChar(ADDR_HAND_SIDE, hand_side); - EEPROM.writeUInt(ADDR_SYSTEM_INIT_FLAG, VERSION_NUM); + EEPROM.writeUInt(ADDR_SYSTEM_INIT_FLAG, FW_VERSION_NUM); if (EEPROM.commit()) { @@ -115,27 +117,27 @@ bool read_EEPROM(){ // system_init_flag = EEPROM.readUInt(ADDR_SYSTEM_INIT_FLAG); - if (EEPROM.readUInt(ADDR_SYSTEM_INIT_FLAG) != VERSION_NUM) + if (EEPROM.readUInt(ADDR_SYSTEM_INIT_FLAG) != FW_VERSION_NUM) { // return false; Serial.println("System didn't initialised"); write_default_EEPROM(); } - // EEPROM.readString(ADDR_WIFI_SSID_1).toCharArray(WiFi_SSID_1, sizeof(WiFi_SSID_1)); - +/* DefaultTemp = EEPROM.readUShort(ADDR_DEFAULT_TEMP); SleepTemp = EEPROM.readUShort(ADDR_SLEEP_TEMP); BoostTemp = EEPROM.readUChar(ADDR_BOOST_TEMP); time2sleep = EEPROM.readUShort(ADDR_TIME_2_SLEEP); time2off = EEPROM.readUChar(ADDR_TIME_2_OFF); timeOfBoost = EEPROM.readUChar(ADDR_TIME_OF_BOOST); +*/ MainScrType = EEPROM.readUChar(ADDR_MAIN_SCREEN); // PIDenable = EEPROM.readBool(ADDR_PID_ENABLE); beepEnable = EEPROM.readBool(ADDR_BEEP_ENABLE); - VoltageValue = EEPROM.readUChar(ADDR_VOLTAGE_VALUE); +// VoltageValue = EEPROM.readUChar(ADDR_VOLTAGE_VALUE); QCEnable = EEPROM.readBool(ADDR_QC_ENABLE); - WAKEUPthreshold = EEPROM.readUChar(ADDR_WAKEUP_THRESHOLD); + //WAKEUPthreshold = EEPROM.readUChar(ADDR_WAKEUP_THRESHOLD); CurrentTip = EEPROM.readUChar(ADDR_CURRENT_TIP); NumberOfTips = EEPROM.readUChar(ADDR_NUMBER_OF_TIPS); @@ -155,9 +157,10 @@ bool read_EEPROM(){ } bool update_default_temp_EEPROM(){ + return true; Serial.println("Updating default temp in EEPROM"); - EEPROM.writeUShort(ADDR_DEFAULT_TEMP, DefaultTemp); + //EEPROM.writeUShort(ADDR_DEFAULT_TEMP, DefaultTemp); if (EEPROM.commit()) { diff --git a/SolderingPen_ESP32S2/UtilsEEPROM.h b/SolderingPen_ESP32S2/UtilsEEPROM.h index 7b2705f..accf5d9 100644 --- a/SolderingPen_ESP32S2/UtilsEEPROM.h +++ b/SolderingPen_ESP32S2/UtilsEEPROM.h @@ -27,17 +27,17 @@ #define ADDR_EEPROM_SIZE ADDR_HAND_SIDE + 1 -extern uint16_t DefaultTemp; -extern uint16_t SleepTemp; -extern uint8_t BoostTemp; -extern uint16_t time2sleep; -extern uint8_t time2off; -extern uint8_t timeOfBoost; +//extern uint16_t DefaultTemp; +//extern uint16_t SleepTemp; +//extern uint8_t BoostTemp; +//extern uint16_t time2sleep; +//extern uint8_t time2off; +//extern uint8_t timeOfBoost; extern uint8_t MainScrType; extern bool beepEnable; -extern volatile uint8_t VoltageValue; +//extern volatile uint8_t VoltageValue; extern bool QCEnable; -extern uint8_t WAKEUPthreshold; +//extern uint8_t WAKEUPthreshold; extern uint8_t CurrentTip; extern uint8_t NumberOfTips; diff --git a/SolderingPen_ESP32S2/common.hpp b/SolderingPen_ESP32S2/common.hpp new file mode 100644 index 0000000..c69cdc3 --- /dev/null +++ b/SolderingPen_ESP32S2/common.hpp @@ -0,0 +1,31 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once +#include "config.h" +#include + +enum class ironState_t { + idle = 0, // everything is on, exept heater, Iron is in idle state, i.e. it was just powered ON + working, // everything is on, full function + standby, // heater is in standby mode, i.e. keeping warm after a certain period of inactivity + suspend, // hibernation mode + boost, // heater temperature is increased for a short period of time + setup, // iron is configuration mode, i.e. working with screen menu, heater switches off + notip // tip is missing or failed + +}; + +// working temperature values +struct Temperatures { + int32_t working{TEMP_DEFAULT}, standby{TEMP_SLEEP}, boost{TEMP_BOOST}; +}; + diff --git a/SolderingPen_ESP32S2/config.h b/SolderingPen_ESP32S2/config.h index 6dace48..b58217f 100644 --- a/SolderingPen_ESP32S2/config.h +++ b/SolderingPen_ESP32S2/config.h @@ -1,8 +1,8 @@ #pragma once // Firmware version -#define VERSION "v4.5.3" //20240130 -#define VERSION_NUM 422 +#define FW_VERSION "v0.0.1_experimental" +#define FW_VERSION_NUM 422 // Type of MOSFET #define P_MOSFET // P_MOSFET or N_MOSFET @@ -21,9 +21,9 @@ #define SENSOR_PIN 1 // tip temperature sense 烙铁头温感 #define VIN_PIN 6 // input voltage sense 检测输入电压 #define BUZZER_PIN 3 // buzzer 蜂鸣器 -#define BUTTON_PIN 0 // switch 按键right -#define BUTTON_P_PIN 4 // 1 键位为“+” -#define BUTTON_N_PIN 2 // 2 键位为“-” +#define BUTTON_ACTION GPIO_NUM_0 // middle push-button +#define BUTTON_INCR GPIO_NUM_2 // incrementer “+” push-button +#define BUTTON_DECR GPIO_NUM_4 // decrementer “-” push-button #define HEATER_PIN 5 // heater MOSFET PWM control 加热器MOSFET PWM控制 #define SH1107_RST_PIN 7 // display reset pin @@ -44,11 +44,11 @@ // Default temperature control value (recommended soldering temperature: 300~380°C) // 默认温度控制值(推荐焊接温度:300~380°C) -#define TEMP_MIN 50 // 最小温度 +#define TEMP_MIN 150 // 最小温度 #define TEMP_MAX 450 // 最大温度 -#define TEMP_NOTIP 500 // virtual temperature when tip is not installed -#define TEMP_DEFAULT 260 // 默认温度 -#define TEMP_SLEEP 150 // 休眠温度 +#define TEMP_NOTIP 500 // virtual temperature threshold when tip is not installed +#define TEMP_DEFAULT 220 // 默认温度 +#define TEMP_SLEEP 120 // 休眠温度 #define TEMP_BOOST 50 // 升温步进 #define TEMP_STEP 10 // temperature change step / 旋转编码器温度变化步进 #define POWER_LIMIT_15 170 // 功率限制 @@ -65,10 +65,11 @@ #define TIPNAMELENGTH 6 // max length of tip names (including termination) #define TIPNAME "PTS " // default tip name -// Default timer value (0 = disabled) / 默认的定时器值 (0 = 禁用) -#define TIME2SLEEP 60 // sleep mode timer, seconds / 几秒钟后进入睡眠模式 -#define TIME2OFF 5 // off timer, minutes / 几分钟后就要关闭加热器了 -#define TIMEOFBOOST 60 // boos mode duration / 停留在加热模式多少秒 +// Default timeout values, in milliseconds +#define TIMEOUT_STANDBY 60000 // standby time, when heater lowers it's temperature +#define TIMEOUT_IDLE 300000 // idle mode timeout, when Iron switches off the heater after a certain period of inactivity +#define TIMEOUT_SUSPEND 1200000 // suspend timeout +#define TIMEOUT_BOOST 60000 // boost mode duration / 停留在加热模式多少秒 #define WAKEUP_THRESHOLD 10 // MPU vibration detection accuracy, the smaller the value, the more sensitive it is / MPU 震动检测精度,数值越小,越灵敏 // Control values @@ -76,7 +77,7 @@ #define TIME2SETTLE_20V 2000 // The time in microseconds allowed for the OpAmp output to stabilize / 以微秒为单位的时间允许OpAmp输出稳定 #define SMOOTHIE 0.2 // OpAmp output smoothing coefficient (1=no smoothing; default: 0.05) / OpAmp输出平滑系数 (1=无平滑; 默认:0.05) //#define PID_ENABLE true // enable PID control -#define PID_ENGAGE_DIFF 50 // temperature difference when PID algo should be engaged +#define PID_ENGAGE_DIFF 30 // temperature difference when PID algo should be engaged #define BEEP_ENABLE true // enable/disable buzzer #define VOLTAGE_VALUE 3 // 电压值 #define QC_ENABLE false // enable/disable QC3.0 diff --git a/SolderingPen_ESP32S2/const.h b/SolderingPen_ESP32S2/const.h index c968afa..c8dbd83 100644 --- a/SolderingPen_ESP32S2/const.h +++ b/SolderingPen_ESP32S2/const.h @@ -2,9 +2,23 @@ // static literals are defined here +// LOG tags static constexpr const char* T_ADC = "ADC"; static constexpr const char* T_CTRL = "CTRL"; static constexpr const char* T_DBG = "DBG"; static constexpr const char* T_GYRO = "GYRO"; static constexpr const char* T_PWM = "PWM"; static constexpr const char* T_HEAT = "HEAT"; + +// NVS namespaces +static constexpr const char* T_IRON = "IRON"; +static constexpr const char* T_Sensor = "Sensor"; +static constexpr const char* T_UI = "UI"; +static constexpr const char* T_HID = "HID"; + +// NVS keys +static constexpr const char* T_timeouts = "timeouts"; +static constexpr const char* T_temperatures = "temperatures"; + +static constexpr const char* T_motionThr = "motionThr"; // motion threshold (uint32) +static constexpr const char* T_lang = "lang"; // UI language diff --git a/SolderingPen_ESP32S2/evtloop.cpp b/SolderingPen_ESP32S2/evtloop.cpp index 255b187..b7816a2 100644 --- a/SolderingPen_ESP32S2/evtloop.cpp +++ b/SolderingPen_ESP32S2/evtloop.cpp @@ -26,8 +26,8 @@ static const char* TAG = "evt"; ESP_EVENT_DEFINE_BASE(SENSOR_DATA); ESP_EVENT_DEFINE_BASE(IRON_SET_EVT); ESP_EVENT_DEFINE_BASE(IRON_GET_EVT); -ESP_EVENT_DEFINE_BASE(IRON_STATE_EVT); -ESP_EVENT_DEFINE_BASE(IRON_CHANGE_EVT); +ESP_EVENT_DEFINE_BASE(IRON_NOTIFY); +ESP_EVENT_DEFINE_BASE(IRON_STATE); namespace evt { diff --git a/SolderingPen_ESP32S2/evtloop.hpp b/SolderingPen_ESP32S2/evtloop.hpp index 954e27d..79fd62d 100644 --- a/SolderingPen_ESP32S2/evtloop.hpp +++ b/SolderingPen_ESP32S2/evtloop.hpp @@ -40,12 +40,29 @@ enum class iron_t:int32_t { // Sensors data 100-199 motion=100, // motion detected from GyroSensor - vin=110, // Vin voltage in millvolts, parameter uint32_t - tiptemp=120, // current Tip temperature, parameter int32_t acceltemp=121, // accelerometer chip temperature, parameter float + + // Commands + sensorsReload = 200, // reload configuration for any sensors available + heaterTargetT, // set heater target temperature, parameter int32_t + workTemp, // set working temperature, parameter int32_t + workModeToggle, // toggle working mode on/off + boostModeToggle, // toggle boost mode on/off + + // State notifications + stateWorking = 300, + stateStandby, // iron controller switched to 'Standby' mode + stateIdle, + stateSuspend, + stateBoost, + stateSetup, + stateNoTip, + tipEject, // sent by heater when it looses the tip sense + tipInsert, // sent by heater when detect tip sensor + // END noop_end // stub }; diff --git a/SolderingPen_ESP32S2/heater.cpp b/SolderingPen_ESP32S2/heater.cpp index bb48d3b..b7f7e32 100644 --- a/SolderingPen_ESP32S2/heater.cpp +++ b/SolderingPen_ESP32S2/heater.cpp @@ -1,8 +1,9 @@ #include "heater.hpp" #include -#include "log.h" +#include "evtloop.hpp" #include "const.h" #include "main.h" +#include "log.h" #define HEATER_TASK_PRIO tskIDLE_PRIORITY+1 // task priority #ifdef PTS200_DEBUG_LEVEL @@ -28,10 +29,87 @@ constexpr TickType_t long_measure_delay_ticks = pdMS_TO_TICKS(500); // idle interval between temp measurments constexpr TickType_t idle_delay_ticks = pdMS_TO_TICKS(1000); +TipHeater::~TipHeater(){ + // unsubscribe from event bus + if (_evt_cmd_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_SET_EVT, ESP_EVENT_ANY_ID, _evt_cmd_handler); + _evt_cmd_handler = nullptr; + } + if (_evt_ntf_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_NOTIFY, ESP_EVENT_ANY_ID, _evt_ntf_handler); + _evt_ntf_handler = nullptr; + } + + _stop_runner(); +} void TipHeater::init(){ - // Prepare and then apply the LEDC PWM timer configuration + adc_sensor.attach(SENSOR_PIN); + + // set PID output range + _pid.setOutputRange(0, 1<(self)->_evt_picker(base, id, data); }, + this, + &_evt_cmd_handler + ); + } + + if (!_evt_ntf_handler){ + esp_event_handler_instance_register_with( + evt::get_hndlr(), + IRON_NOTIFY, + ESP_EVENT_ANY_ID, // subscribe to 'sensorsReload command' + [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->_evt_picker(base, id, data); }, + this, + &_evt_ntf_handler + ); + } + + // create RTOS task that controls heater PWM + _start_runner(); +} + +void TipHeater::_evt_picker(esp_event_base_t base, int32_t id, void* data){ + + switch (static_cast(id)){ + case evt::iron_t::heaterTargetT : { + // change heater temp + setTargetTemp(*reinterpret_cast(data)); + LOGD(T_HEAT, printf, "set target T:%d\n", _t.target); + return; + } + + case evt::iron_t::stateWorking : { + // enable heater + enable(); + return; + } + + case evt::iron_t::stateIdle : { + // disable heater in standby mode + disable(); + return; + } + + case evt::iron_t::stateSuspend : { + // disable worker task in standby mode + _stop_runner(); + return; + } + } +} + + +void TipHeater::_start_runner(){ + // Prepare and then apply the LEDC PWM timer configuration ledc_timer_config_t ledc_timer = { .speed_mode = LEDC_MODE, .duty_resolution = LEDC_DUTY_RES, @@ -43,36 +121,17 @@ void TipHeater::init(){ // Prepare and then apply the LEDC PWM channel configuration ledc_channel_config_t ledc_channel = { - .gpio_num = _gpio, + .gpio_num = _pwm.gpio, .speed_mode = LEDC_MODE, - .channel = _ledc_channel, + .channel = _pwm.channel, .intr_type = LEDC_INTR_DISABLE, .timer_sel = LEDC_TIMER, .duty = 0, .hpoint = 0, - .flags = {.output_invert = _invert } + .flags = {.output_invert = _pwm.invert } }; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); - adc_sensor.attach(SENSOR_PIN); - - // set PID output range - _pid.setOutputRange(0, 1< TEMP_NOTIP){ + // check if we've lost the Tip + if (_state != HeaterState_t::notip && t > TEMP_NOTIP){ + // we have just lost connection with a tip sensor + // disable PWM + ledc_set_duty(LEDC_MODE, _pwm.channel, 0); + ledc_update_duty(LEDC_MODE, _pwm.channel); + _state = HeaterState_t::notip; + LOGI(T_HEAT, printf, "Iron Tip ejected, T:%d\n", t); + EVT_POST(SENSOR_DATA, e2int(evt::iron_t::tipEject)); + continue; + } + + // check if we've get the Tip back + if (_state == HeaterState_t::notip && t < TEMP_NOTIP){ + _state = HeaterState_t::inactive; + EVT_POST(SENSOR_DATA, e2int(evt::iron_t::tipInsert)); + LOGI(T_HEAT, println, "Iron Tip inserted"); + continue; + } + + // if heater is inactive, just reset avg temperature readings and suspend + if (_state == HeaterState_t::inactive){ + //_t.calibrated = calculateTemp(t); _t.calibrated = t; _t.avg = t; - // set pwm to 0 if active - if (ledc_get_duty(LEDC_MODE, _ledc_channel)){ - ledc_set_duty(LEDC_MODE, _ledc_channel, 0); - ledc_update_duty(LEDC_MODE, _ledc_channel); - } - delay_time = idle_delay_ticks; + EVT_POST_DATA(SENSOR_DATA, e2int(evt::iron_t::tiptemp), &_t.calibrated, sizeof(_t.calibrated)); continue; } + // TODO: check for fault-overheating here + + // OK, now we are in active state for sure + // read tip temperature and average it with previous readings _t.avg += (t - _t.avg) * SMOOTHIE; // stabilize ADC temperature reading 稳定ADC温度读数 // calibrate temp based on map table // note: this is ugly external function, I will rework it later - _t.calibrated = calculateTemp(_t.avg); + //_t.calibrated = calculateTemp(_t.avg); + _t.calibrated = _t.avg; LOGV(T_HEAT, printf, "avg T: %5.1f, calibrated T: %d\n", _t.avg, _t.calibrated); + EVT_POST_DATA(SENSOR_DATA, e2int(evt::iron_t::tiptemp), &_t.calibrated, sizeof(_t.calibrated)); auto diff = abs(_t.target - _t.calibrated); // if PID algo should be engaged if (diff < PID_ENGAGE_DIFF){ - _pwm_duty = _pid.step(_t.target, _t.calibrated); + _pwm.duty = _pid.step(_t.target, _t.calibrated); delay_time = measure_delay_ticks; } else { // heater must be either turned on or off - _t.calibrated < _t.target ? _pwm_duty = 1<(t), 20, 200, 20, CalTemp[CurrentTip][0]); + + if (t < 280) + return map(static_cast(t), 200, 280, CalTemp[CurrentTip][0], CalTemp[CurrentTip][1]); + + return map(static_cast(t), 280, 360, CalTemp[CurrentTip][1], CalTemp[CurrentTip][2]); +} + +*/ \ No newline at end of file diff --git a/SolderingPen_ESP32S2/heater.hpp b/SolderingPen_ESP32S2/heater.hpp index 11141e3..216e133 100644 --- a/SolderingPen_ESP32S2/heater.hpp +++ b/SolderingPen_ESP32S2/heater.hpp @@ -17,32 +17,45 @@ constexpr float consKp = 5, consKi = 1, consKd = 6; */ class TipHeater { -struct Temperatures -{ - // target Tip temperature - int32_t target; - // tip temperature with applied calibration mapping - int32_t calibrated; - // averaged measured temperature - float avg; - -}; - - bool enabled = false; + enum class HeaterState_t { + shutoff = 0, // both heater control, PWM and temperature measurments are disabled + inactive, // heater is disabled, but tip temperature control is active + active, // heater is active, temperature control is engaged + notip, // failure to detect tip temperature or tip is missing + fault // temperature control was unable to stabilyze temperature, overhating, FET failure, etc... + }; + + struct Temperatures + { + // target Tip temperature the heater will try to match + int32_t target; + // averaged measured tip temperature + float avg; + // averaged temperature with applied calibration mapping + int32_t calibrated; + }; + + // PWM related configuration + struct CfgPWM { + int gpio; + ledc_channel_t channel; + bool invert; + uint32_t duty; + }; + + HeaterState_t _state{HeaterState_t::inactive}; // temperatures Temperatures _t{}; - // PWM - int _gpio; - ledc_channel_t _ledc_channel; - bool _invert; + CfgPWM _pwm; - // working PWM duty - uint32_t _pwm_duty{0}; + TaskHandle_t _task_hndlr = nullptr; + // event handlers + esp_event_handler_instance_t _evt_cmd_handler = nullptr; + esp_event_handler_instance_t _evt_ntf_handler = nullptr; - TaskHandle_t _task_hndlr = nullptr; ESP32AnalogRead adc_sensor; @@ -51,21 +64,37 @@ struct Temperatures FastPID _pid = FastPID(consKp, consKi, consKd, HEATER_MEASURE_RATE); // static wrapper for _runner Task to call handling class member - static inline void _runner(void* pvParams){ ((TipHeater*)pvParams)->_runnerHndlr(); } + static inline void _runner(void* pvParams){ ((TipHeater*)pvParams)->_heaterControl(); } + + // events processing + void _evt_picker(esp_event_base_t base, int32_t id, void* data); - // member function that runs RTOS task - void _runnerHndlr(); + /** + * @brief RTOS task runner that monitor tip temperature and controls heater PWM + * + * @return * void + */ + void _heaterControl(); - // starts RTOS task + /** + * @brief starts RTOS PWM controlling task + * it also includes tip temperature measuring + */ void _start_runner(); + /** + * @brief stops RTOS PWM controlling task + * it also stops tip temperature measuing + */ + void _stop_runner(); + // void _measureTipTemp(); float _denoiseADC(); public: - TipHeater(int gpio, ledc_channel_t ledc_channel = LEDC_CHANNEL_0, bool invert = false) : _gpio(gpio), _ledc_channel(ledc_channel), _invert(invert) {}; + TipHeater(int gpio, ledc_channel_t channel = LEDC_CHANNEL_0, bool invert = false) : _pwm({gpio, channel, invert, 0}) {}; ~TipHeater(); /** diff --git a/SolderingPen_ESP32S2/hid.cpp b/SolderingPen_ESP32S2/hid.cpp new file mode 100644 index 0000000..815a91d --- /dev/null +++ b/SolderingPen_ESP32S2/hid.cpp @@ -0,0 +1,394 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ + +#include "hid.hpp" +#include "nvs.hpp" +#include "const.h" +#include // https://github.com/olikraus/u8g2 +#include "log.h" + +/* +#ifdef U8X8_HAVE_HW_SPI +#include +#endif +#ifdef U8X8_HAVE_HW_I2C +#include +#endif +*/ +#include + +// font +#include "PTS200_16.h" +//#include "Languages.h" + +// Setup u8g object depending on OLED controller +#if defined(SSD1306) +U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE, /* clock=*/22, /* data=*/21); +#elif defined(SH1107) +U8G2_SH1107_64X128_F_HW_I2C u8g2(U8G2_R1, SH1107_RST_PIN); +#else +#error Wrong OLED controller type! +#endif + +#define X_OFFSET_TIP_TEMP 50 +#define Y_OFFSET_TIP_TEMP 18 + +#define OFFSET_TARGET_TEMP_1 78 +#define OFFSET_TARGET_TEMP_2 95 +#define Y_OFFSET_SNS_TEMP 50 +#define X_OFFSET_VIN 94 +#define Y_OFFSET_VIN 50 + + +// shortcut alias +using ESPButton::event_t; +using evt::iron_t; + + +void IronScreen::init(){ + u8g2.initDisplay(); + u8g2.begin(); + u8g2.sendF("ca", 0xa8, 0x3f); + u8g2.enableUTF8Print(); + + _viset = std::make_unique(); + +/* TBD + if(_hand_side){ + u8g2.setDisplayRotation(U8G2_R3); + }else{ + u8g2.setDisplayRotation(U8G2_R1); + } +*/ + +} + +// ********************* +// *** IronHID *** + +IronHID::IronHID() : _encdr(BUTTON_DECR, BUTTON_INCR, LOW), _btn(BUTTON_ACTION, LOW) { + _set_button_menu_callbacks(); +} + +void IronHID::init(const Temperatures& t){ + // initialize screen + _screen.init(); + + // link our event loop with ESPButton + ESPButton::set_event_loop_hndlr(evt::get_hndlr()); + + // subscribe to button events + if (!_btn_evt_handler){ + ESP_ERROR_CHECK(esp_event_handler_instance_register_with( + evt::get_hndlr(), + EBTN_EVENTS, ESP_EVENT_ANY_ID, + [](void* self, esp_event_base_t base, int32_t id, void* data) { + // pick action button events and pass it to _menu member + LOGD(T_HID, printf, "btn event:%d, gpio:%d\n", id, reinterpret_cast(data)->gpio); + if (reinterpret_cast(data)->gpio == BUTTON_ACTION) + static_cast(self)->_menu.handleEvent(ESPButton::int2event_t(id), reinterpret_cast(data)); + }, + this, &_btn_evt_handler) + ); + } + + if (!_enc_evt_handler){ + ESP_ERROR_CHECK(esp_event_handler_instance_register_with( + evt::get_hndlr(), + EBTN_ENC_EVENTS, ESP_EVENT_ANY_ID, + [](void* self, esp_event_base_t base, int32_t id, void* data) { + LOGD(T_HID, printf, "enc event:%d, cnt:%d\n", id, reinterpret_cast(data)->cntr); + // pick encoder events and pass it to _menu member + static_cast(self)->_encoder_events(base, id, data); + }, + this, &_enc_evt_handler) + ); + } + + // get initial working temp. + _wrk_temp = t.working; + + // enable middle button + _btn.enable(); + // enable 'encoder' buttons + _encdr.begin(); + // set level 0 for button and encoder buttons + _switch_buttons_modes(0); +} + +void IronHID::_set_button_menu_callbacks(){ + // actions for middle button in main mode + _menu.assign(BUTTON_ACTION, 0, [this](event_t e, const EventMsg* m){ _menu_0_main_mode(e, m); }); + +} + +// encoder events executor, it works in cooperation with _menu object +void IronHID::_encoder_events(esp_event_base_t base, int32_t id, void* event_data){ + switch(_menu.getMenuLevel()){ + // main working mode - encoder controls Iron temperature + case 0 : { + _wrk_temp = reinterpret_cast(event_data)->cntr; + EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::workTemp), &_wrk_temp, sizeof(_wrk_temp)); + break; + } + } + + +} + +void IronHID::_switch_buttons_modes(uint32_t level){ + switch(level){ + // main working mode + case 0 : + _btn.deactivateAll(); + _btn.enableEvent(event_t::click); + _btn.enableEvent(event_t::longPress); + _btn.enableEvent(event_t::multiClick); + // set encoder to control working temperature + _encdr.reset(); + _encdr.setCounter(_wrk_temp, 5, TEMP_MIN, TEMP_MAX); + _encdr.setMultiplyFactor(2); + break; + + } +} + +// actions for middle button in main mode +void IronHID::_menu_0_main_mode(ESPButton::event_t e, const EventMsg* m){ + LOGD(T_HID, printf, "Button Menu lvl:0 e:%d\n", e); + switch(e){ + // Use click event to toggle iron working mode on/off + case event_t::click : + EVT_POST(IRON_SET_EVT, e2int(iron_t::workModeToggle)); + break; + + // use longPress to toggle boost mode + case event_t::longPress : + EVT_POST(IRON_SET_EVT, e2int(iron_t::boostModeToggle)); + break; + + // pick multiClicks + case event_t::multiClick : + // todo: doubleclick for entering Menu + //EVT_POST(IRON_SET_EVT, e2int(iron_t::boostModeToggle)); + break; + + } + +} + + +// ***** VisualSet ***** +VisualSet::VisualSet() { + // subscribe to all events on a bus + ESP_ERROR_CHECK(esp_event_handler_instance_register_with( + evt::get_hndlr(), + ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, + VisualSet::_event_picker, + this, &_evt_handler) + ); + + // loag UI language index + esp_err_t err; + std::unique_ptr nvs = nvs::open_nvs_handle(T_UI, NVS_READONLY, &err); + + if (err == ESP_OK) { + nvs->get_item(T_lang, lang); + } +} + +VisualSet::~VisualSet(){ + // unsubscribe from an event bus + if (_evt_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, _evt_handler); + _evt_handler = nullptr; + } +} + +void VisualSet::_event_picker(void* arg, esp_event_base_t base, int32_t id, void* event_data){ + //LOGV(printf, "VisualSet::_event_picker %s:%d\n", base, id); + if (base == SENSOR_DATA) + return reinterpret_cast(arg)->_evt_sensor(id, event_data); + + if (base == IRON_NOTIFY) + return reinterpret_cast(arg)->_evt_notify(id, event_data); + + if (base == IRON_SET_EVT) + return reinterpret_cast(arg)->_evt_cmd(id, event_data); + + if (base == IRON_STATE) + return reinterpret_cast(arg)->_evt_state(id, event_data); +} + +VisualSetMainScreen::VisualSetMainScreen() : VisualSet(){ + // request working temperature from IronController + EVT_POST(IRON_GET_EVT, e2int(iron_t::workTemp)); + + // the screen will redraw at first event from sensors + //_drawMainScreen(); +} + +void VisualSetMainScreen::_drawMainScreen(){ + u8g2.clearBuffer(); + + if(lang == 0){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } else + u8g2.setFont(PTS200_16); + + u8g2.setFontPosTop(); + + // draw status of heater 绘制加热器状态 + u8g2.setCursor(0, 0 + SCREEN_OFFSET); + switch (_state){ + case ironState_t::idle : + u8g2.print(dict[lang][D_idle]); + break; + case ironState_t::working : + u8g2.print(dict[lang][D_heating]); + break; + case ironState_t::standby : + u8g2.print(dict[lang][D_standby]); + break; + case ironState_t::boost : + u8g2.print(dict[lang][D_boost]); + break; + case ironState_t::notip : + u8g2.print(dict[lang][D_notip]); + break; + + default:; + } + + // print working temperature in the upper right corner + // u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, "设温:"); + //u8g2.drawUTF8(OFFSET_TARGET_TEMP_1, 0 + SCREEN_OFFSET, dict[lang][D_set_t]); // target temperature + //u8g2.setFont(u8g2_font_unifont_t_chinese3); + u8g2.setCursor(OFFSET_TARGET_TEMP_1, 0 + SCREEN_OFFSET); + //u8g2.drawUTF8(OFFSET_TARGET_TEMP_1, 0 + SCREEN_OFFSET, "T:"); // target temperature + //u8g2.setCursor(OFFSET_TARGET_TEMP_2, 0 + SCREEN_OFFSET); + u8g2.print("T:"); + u8g2.print(_wrk_temp, 0); + u8g2.print("C"); + +/* + if(lang != 0){ + u8g2.setFont(PTS200_16); + } + + if (_tip_temp > TEMP_NOTIP) + u8g2.print(dict[lang][D_error]); + else if (inOffMode) + u8g2.print(txt_off[language]); + else if (inSleepMode) + u8g2.print(txt_sleep[language]); + else if (inBoostMode) + u8g2.print(txt_boost[language]); + else if (isWorky) + u8g2.print(txt_worky[language]); + else if (heater.getCurrentTemp() - heater.getTargetTemp() < PID_ENGAGE_DIFF) + u8g2.print(txt_on[language]); + else + u8g2.print(txt_hold[language]); + + u8g2.setFont(u8g2_font_unifont_t_chinese3); +*/ + + // casing internal temperature sensor + u8g2.setCursor(0, Y_OFFSET_SNS_TEMP); + u8g2.print(_sns_temp, 1); + u8g2.print("C"); + + // input voltage + u8g2.setCursor(X_OFFSET_VIN, Y_OFFSET_VIN); + u8g2.print(_vin/1000.0, 1); // convert mv in V + u8g2.print("V"); + + // draw current tip temperature 绘制当前温度 + u8g2.setFont(u8g2_font_freedoomr25_tn); + u8g2.setFontPosTop(); + u8g2.setCursor(X_OFFSET_TIP_TEMP, Y_OFFSET_TIP_TEMP); + if (_tip_temp > TEMP_NOTIP) + u8g2.print("Err"); + else + u8g2.printf("%03d", _tip_temp); + +/* + // draw current temperature in big figures 用大数字绘制当前温度 + u8g2.setFont(u8g2_font_fub42_tn); + u8g2.setFontPosTop(); + u8g2.setCursor(15, 20); + if (_tip_temp > TEMP_NOTIP) + u8g2.print("Err"); + else + u8g2.printf("%03d", _tip_temp); +*/ + + u8g2.sendBuffer(); +} + +void VisualSetMainScreen::_evt_sensor(int32_t id, void* data){ + switch(static_cast(id)){ + case evt::iron_t::tiptemp : + _tip_temp = *reinterpret_cast(data); + break; + case evt::iron_t::vin : + _vin = *reinterpret_cast(data); + break; + case evt::iron_t::acceltemp : + _sns_temp = *reinterpret_cast(data); + break; + + default: + return; + } + + _drawMainScreen(); +} + +void VisualSetMainScreen::_evt_notify(int32_t id, void* data){ + switch(static_cast(id)){ + case evt::iron_t::stateIdle : + _state = ironState_t::idle; + break; + case evt::iron_t::stateWorking : + _state = ironState_t::working; + break; + case evt::iron_t::stateStandby : + _state = ironState_t::standby; + break; + case evt::iron_t::stateBoost : + _state = ironState_t::boost; + break; + } +} + +void VisualSetMainScreen::_evt_cmd(int32_t id, void* data){ + switch(static_cast(id)){ + case evt::iron_t::workTemp : + _wrk_temp = *reinterpret_cast(data); + break; + } +} + +void VisualSetMainScreen::_evt_state(int32_t id, void* data){ + switch(static_cast(id)){ + case evt::iron_t::workTemp : + _wrk_temp = *reinterpret_cast(data); + break; + } +} + +// ***************************** +// *** An instance of HID object + +IronHID hid; \ No newline at end of file diff --git a/SolderingPen_ESP32S2/hid.hpp b/SolderingPen_ESP32S2/hid.hpp new file mode 100644 index 0000000..20abcb7 --- /dev/null +++ b/SolderingPen_ESP32S2/hid.hpp @@ -0,0 +1,160 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once +#include "common.hpp" +#include "evtloop.hpp" +#include "espasyncbutton.hpp" +#include "lang/dictionaries.h" +#include + + +/** + * @brief A generic screen object instance + * abstract class that represents some screen information, + * a set of parameters to monitor and display, a menu, etc... + * Specific implementation depends on derived class + */ +class VisualSet { + esp_event_handler_instance_t _evt_handler = nullptr; + + // event dispatcher + static void _event_picker(void* arg, esp_event_base_t base, int32_t id, void* event_data); + +protected: + lang_index lang{L_en_us}; + + // sensor events picker + virtual void _evt_sensor(int32_t id, void* event_data){}; + // notifications + virtual void _evt_notify(int32_t id, void* event_data){}; + // commands + virtual void _evt_cmd(int32_t id, void* data){}; + // commands + virtual void _evt_state(int32_t id, void* data){}; + + +public: + VisualSet(); + virtual ~VisualSet(); + +}; + + +/** + * @brief Main Iron screen display + * show working mode and basic sensors information + * + */ +class VisualSetMainScreen : public VisualSet { + ironState_t _state{0}; + int32_t _wrk_temp{0}, _tip_temp{0}; + // sensor temp + float _sns_temp{0}; + // input voltage + uint32_t _vin{0}; + + // renders Main working screen + void _drawMainScreen(); + + // sensor events handler + void _evt_sensor(int32_t id, void* data) override; + + // notify events handler + void _evt_notify(int32_t id, void* data) override; + + // command events handler + void _evt_cmd(int32_t id, void* data) override; + + // state events handler + void _evt_state(int32_t id, void* data) override; + +public: + VisualSetMainScreen(); + +}; + +/** + * @brief and object that controls the screen and generates visual info + * + */ +class IronScreen { + + bool _hand_side; + + // an instance of visual oject that represents displayed info on a screen + std::unique_ptr _viset; + +public: + + void init(); + +}; + +/** + * @brief an object that manages button controls and Screen navigation + * + */ +class IronHID { + + // Display object + IronScreen _screen; + + // Two button pseudo-encoder + PseudoRotaryEncoder _encdr; + + // action button + GPIOButton _btn; + + // action button menu + ButtonCallbackMenu _menu; + + // action button event handler + esp_event_handler_instance_t _btn_evt_handler = nullptr; + + // encoder event handler + esp_event_handler_instance_t _enc_evt_handler = nullptr; + + // target temperature + int32_t _wrk_temp; + + // encoder events executor, it works in cooperation with _menu object + void _encoder_events(esp_event_base_t base, int32_t id, void* event_data); + + // init action button menu + void _set_button_menu_callbacks(); + + // switch between different menu modes for action button + void _switch_buttons_modes(uint32_t level); + + /** + * @brief button actions when iron is main working mode + * + */ + void _menu_0_main_mode(ESPButton::event_t e, const EventMsg* m); + +public: + // c-tor + IronHID(); + + + /** + * @brief initialize HID, + * attach to button events, init display class, etc... + * + */ + void init(const Temperatures& t); + +}; + + +// Our global instance of HID +extern IronHID hid; diff --git a/SolderingPen_ESP32S2/ironcontroller.cpp b/SolderingPen_ESP32S2/ironcontroller.cpp new file mode 100644 index 0000000..5404ce2 --- /dev/null +++ b/SolderingPen_ESP32S2/ironcontroller.cpp @@ -0,0 +1,239 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ + +#include "ironcontroller.hpp" +#include "main.h" +#include "log.h" + +#define MODE_TIMER_PERIOD 1000 + +using namespace evt; + +IronController::~IronController(){ + // unsubscribe from event bus + if (_evt_sensor_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), SENSOR_DATA, ESP_EVENT_ANY_ID, _evt_sensor_handler); + _evt_sensor_handler = nullptr; + } + + if (_evt_cmd_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_SET_EVT, ESP_EVENT_ANY_ID, _evt_cmd_handler); + _evt_cmd_handler = nullptr; + } + + if (_evt_req_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_GET_EVT, ESP_EVENT_ANY_ID, _evt_req_handler); + _evt_req_handler = nullptr; + } +} + +void IronController::init(){ + + // load timeout values from NVS + nvs_blob_read(T_IRON, T_timeouts, static_cast(&_timeout), sizeof(Timeouts)); + + // load temperature values from NVS + nvs_blob_read(T_IRON, T_temperatures, static_cast(&_temp), sizeof(Temperatures)); + + // start mode switcher timer + if (!_tmr_mode){ + _tmr_mode = xTimerCreate("modeT", + pdMS_TO_TICKS(MODE_TIMER_PERIOD), + pdTRUE, + static_cast(this), + [](TimerHandle_t h) { static_cast(pvTimerGetTimerID(h))->_mode_switcher(); } + ); + if (_tmr_mode) + xTimerStart( _tmr_mode, pdMS_TO_TICKS(10) ); + } + + // event bus subscriptions + if (!_evt_sensor_handler){ + ESP_ERROR_CHECK(esp_event_handler_instance_register_with(evt::get_hndlr(), SENSOR_DATA, ESP_EVENT_ANY_ID, IronController::_event_hndlr, this, &_evt_sensor_handler)); + } + + if (!_evt_cmd_handler){ + ESP_ERROR_CHECK(esp_event_handler_instance_register_with(evt::get_hndlr(), IRON_SET_EVT, ESP_EVENT_ANY_ID, IronController::_event_hndlr, this, &_evt_cmd_handler)); + } + + if (!_evt_req_handler){ + ESP_ERROR_CHECK(esp_event_handler_instance_register_with(evt::get_hndlr(), IRON_GET_EVT, ESP_EVENT_ANY_ID, IronController::_event_hndlr, this, &_evt_req_handler)); + } +} + +void IronController::_mode_switcher(){ + + switch (_state){ + // In working state + case ironState_t::working : { + if (pdTICKS_TO_MS(xTaskGetTickCount()) - pdTICKS_TO_MS(_xTicks.motion) > _timeout.standby){ + _state = ironState_t::standby; + LOGI(T_CTRL, printf, "Engage standby mode due to sleep timeout of %u ms. Temp:%u\n", _timeout.standby, _temp.standby); + // switch heater temperature to standby value + EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::heaterTargetT), &_temp.standby, sizeof(_temp.standby)); + // notify other componets that we are switching to 'standby' mode + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateStandby)); + } + return; + } + + case ironState_t::standby : { + if (pdTICKS_TO_MS(xTaskGetTickCount()) - pdTICKS_TO_MS(_xTicks.motion) > _timeout.idle){ + _state = ironState_t::idle; + LOGI(T_CTRL, printf, "Engage idle mode due to idle timeout of %u ms\n", _timeout.idle); + // notify other componets that we are switching to 'idle' mode + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateIdle)); + } else if (pdTICKS_TO_MS(xTaskGetTickCount()) - pdTICKS_TO_MS(_xTicks.motion) < _timeout.standby){ + // standby cancelled + _state = ironState_t::working; + LOGI(T_CTRL, println, "cancel Standby mode"); + // notify other componets that we are switching to 'work' mode + EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::heaterTargetT), &_temp.working, sizeof(_temp.working)); + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateWorking)); + } + return; + } + + case ironState_t::suspend : { + if (pdTICKS_TO_MS(xTaskGetTickCount()) - pdTICKS_TO_MS(_xTicks.motion) > _timeout.suspend){ + _state = ironState_t::suspend; + LOGI(T_CTRL, printf, "Engage suspend mode due to suspend timeout of %u ms\n", _timeout.suspend); + // notify other componets that we are switching to 'idle' mode + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateSuspend)); + } + return; + } + + case ironState_t::boost : { + if (pdTICKS_TO_MS(xTaskGetTickCount()) - pdTICKS_TO_MS(_xTicks.boost) > _timeout.boost){ + _state = ironState_t::working; + // notify other componets that we are switching to 'working' mode + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateWorking)); + LOGI(T_CTRL, printf, "Engage work mode due to boost timeout of %u ms\n", _timeout.boost); + } + } + + default:; + } +} + +void IronController::_event_hndlr(void* handler, esp_event_base_t base, int32_t id, void* event_data){ + CTRL_LOGV(printf, "EVENT %s:%d\n", base, id); + if ( base == SENSOR_DATA ) + return static_cast(handler)->_evt_sensors(base, id, event_data); + + if ( base == IRON_SET_EVT ) + return static_cast(handler)->_evt_commands(base, id, event_data); + + if ( base == IRON_GET_EVT ) + return static_cast(handler)->_evt_reqs(base, id, event_data); +} + +void IronController::_evt_sensors(esp_event_base_t base, int32_t id, void* data){ + switch (static_cast(id)){ + case evt::iron_t::motion : + // update motion detect timestamp + _xTicks.motion = xTaskGetTickCount(); + } +} + +// process command events +void IronController::_evt_commands(esp_event_base_t base, int32_t id, void* data){ + switch (static_cast(id)){ + + // toggle working mode + case evt::iron_t::workModeToggle : { + // switch working mode on/off + switch (_state){ + case ironState_t::idle : + case ironState_t::standby : + case ironState_t::suspend : + // switch to working mode + _state = ironState_t::working; + // reset motion timer + _xTicks.motion = xTaskGetTickCount(); + // notify other components + LOGI(T_CTRL, println, "switch to working mode"); + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateWorking)); + // set heater to working temperature + EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::heaterTargetT), &_temp.working, sizeof(_temp.working)); + break; + + case ironState_t::boost : + case ironState_t::working : + // switch to idle mode + _state = ironState_t::idle; + // notify other components + LOGI(T_CTRL, println, "switch to Idle mode"); + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateIdle)); + break; + } + break; + } + + // toggle boost mode + case evt::iron_t::boostModeToggle : { + switch (_state){ + case ironState_t::working : { + // switch to boost mode + _state = ironState_t::boost; + // notify other components + LOGI(T_CTRL, println, "switch to Boost mode"); + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateBoost)); + // set heater to boost temperature + int32_t t = _temp.working + _temp.boost; + EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::heaterTargetT), &t, sizeof(t)); + _xTicks.boost = xTaskGetTickCount(); + break; + } + + case ironState_t::boost : + // switch to idle mode + _state = ironState_t::working; + // notify other components + LOGI(T_CTRL, println, "switch to Work mode"); + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateWorking)); + EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::heaterTargetT), &_temp.working, sizeof(_temp.working)); + break; + } + break; + } + + // set work temperature (arrive from HID) + case evt::iron_t::workTemp : { + int32_t t = *reinterpret_cast(data); + if (t != _temp.working){ + _temp.working = *reinterpret_cast(data); + nvs_blob_write(T_IRON, T_temperatures, static_cast(&_temp), sizeof(Temperatures)); + } + if (_state == ironState_t::working) + EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::heaterTargetT), &_temp.working, sizeof(_temp.working)); + break; + } + + // some + } + +} + +void IronController::_evt_reqs(esp_event_base_t base, int32_t id, void* data){ + switch (static_cast(id)){ + + // req for working temperature + case iron_t::workTemp : + EVT_POST_DATA(IRON_STATE, e2int(iron_t::workTemp), &_temp.working, sizeof(_temp.working)); + break; + } +} + +// An instance of IronController +IronController espIron; diff --git a/SolderingPen_ESP32S2/ironcontroller.hpp b/SolderingPen_ESP32S2/ironcontroller.hpp new file mode 100644 index 0000000..5ff23c4 --- /dev/null +++ b/SolderingPen_ESP32S2/ironcontroller.hpp @@ -0,0 +1,109 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once +#include "evtloop.hpp" +#include "common.hpp" +#include "const.h" +#include "nvs.hpp" +#include "freertos/timers.h" + +/** + * @brief An object that represents Iron states and configuration + * + */ +class IronController { + + struct Timeouts { + // all times are in milliseconds! + unsigned idle{TIMEOUT_IDLE}, standby{TIMEOUT_STANDBY}, suspend{TIMEOUT_SUSPEND}, boost{TIMEOUT_BOOST}; + }; + + struct TickStamps { + // last time motion was detected + TickType_t motion; + TickType_t boost; + }; + +public: + Timeouts _timeout; + Temperatures _temp; + TickStamps _xTicks; + +private: + + // current iron mode + ironState_t _state{0}; + + + + // Mode Switcher timer + TimerHandle_t _tmr_mode = nullptr; + + // sensor events handler + esp_event_handler_instance_t _evt_sensor_handler = nullptr; + + // command events handler + esp_event_handler_instance_t _evt_cmd_handler = nullptr; + + // request commands events handler + esp_event_handler_instance_t _evt_req_handler = nullptr; + + /** + * @brief timer callback that watches mode switching + * it maintains timeouts for sleep, off, boost modes, etc... + * + */ + void _mode_switcher(); + + // event bus messages dispatcher + static void _event_hndlr(void* handler, esp_event_base_t base, int32_t id, void* event_data); + + // sensors events executor + void _evt_sensors(esp_event_base_t base, int32_t id, void* data); + + // commands events executor + void _evt_commands(esp_event_base_t base, int32_t id, void* data); + + // req commands events executor + void _evt_reqs(esp_event_base_t base, int32_t id, void* data); + + /** + * @brief save current timeout values to NVS + * + */ + void _saveTimeouts(){ nvs_blob_write(T_IRON, T_timeouts, static_cast(&_timeout), sizeof(Timeouts)); }; + + /** + * @brief save current temperature values to NVS + * + */ + void _saveTemp(){ nvs_blob_write(T_IRON, T_temperatures, static_cast(&_timeout), sizeof(Timeouts)); }; + +public: + ~IronController(); + + void init(); + + + void setTimeOutStandby(unsigned v){ _timeout.standby = v; _saveTimeouts(); } + void setTimeOutSuspend(unsigned v){ _timeout.suspend = v; _saveTimeouts(); } + void setTimeOutBoost(unsigned v){ _timeout.boost = v; _saveTimeouts(); } + + void setTempWorking(unsigned v){ _temp.working = v; _saveTemp(); } + void setTempStandby(unsigned v){ _temp.standby = v; _saveTemp(); } + void setTempBoost(unsigned v){ _temp.boost = v; _saveTemp(); } + + // get internal temperatures configuration + const Temperatures& getTemperatures() const { return _temp; } +}; + +extern IronController espIron; diff --git a/SolderingPen_ESP32S2/lang/dictionaries.h b/SolderingPen_ESP32S2/lang/dictionaries.h new file mode 100644 index 0000000..ebd1e7a --- /dev/null +++ b/SolderingPen_ESP32S2/lang/dictionaries.h @@ -0,0 +1,26 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once +#include "lang_en_us.h" +#include "lang_ru_ru.h" +//#include "lang_zh_cn.h" +//#include "lang_zh_tw.h" + +/** + * Dictionary with references to text strings + * the order of arrays must match ordering lang_index enum + */ +static constexpr std::array< std::array, L____SIZE> dict = { + lang_en_us::dictionary, + lang_ru_ru::dictionary +}; + diff --git a/SolderingPen_ESP32S2/lang/i18n.h b/SolderingPen_ESP32S2/lang/i18n.h new file mode 100644 index 0000000..a2a5248 --- /dev/null +++ b/SolderingPen_ESP32S2/lang/i18n.h @@ -0,0 +1,36 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once + +// List of available translations +enum lang_index { + L_en_us = (0), + L_ru_ru, +// L_zh_cn, +// L_zh_tw, + L____SIZE +}; + +/** + * Text-Dictionary Enums for language resources + * the order of enums must match with elements in dictionary + */ +enum dict_index { + D_boost = (0), + D_error, + D_heating, + D_idle, + D_notip, + D_set_t, + D_standby, + D_____SIZE +}; diff --git a/SolderingPen_ESP32S2/lang/lang_en_us.h b/SolderingPen_ESP32S2/lang/lang_en_us.h new file mode 100644 index 0000000..2bf65da --- /dev/null +++ b/SolderingPen_ESP32S2/lang/lang_en_us.h @@ -0,0 +1,39 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once +#include +#include "i18n.h" + +// EN-US +namespace lang_en_us { + +// EN US +static constexpr const char* T_Boost = "Boost"; +static constexpr const char* T_Error = "Error"; +static constexpr const char* T_Heating = "Heating"; +static constexpr const char* T_Idle = "Idle"; +static constexpr const char* T_NoTip = "No tip!"; // state display when tip is missing +static constexpr const char* T_setT = "Set:"; // Target temperature on main screen +static constexpr const char* T_standby = "Standby"; // state display in 'Standby' + +static constexpr std::array dictionary = { + T_Boost, + T_Error, + T_Heating, + T_Idle, + T_NoTip, + T_setT, + T_standby +}; + +} // end of namespace lang_en_us + diff --git a/SolderingPen_ESP32S2/lang/lang_ru_ru.h b/SolderingPen_ESP32S2/lang/lang_ru_ru.h new file mode 100644 index 0000000..992b06e --- /dev/null +++ b/SolderingPen_ESP32S2/lang/lang_ru_ru.h @@ -0,0 +1,38 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once +#include +#include "i18n.h" + +// RU-RU +namespace lang_ru_ru { + +static constexpr const char* T_Boost = "Разгон"; +static constexpr const char* T_Error = "Ошибка"; +static constexpr const char* T_Heating = "Нагрев"; +static constexpr const char* T_Idle = "Отключен"; +static constexpr const char* T_NoTip = "Нет жала!"; // state display when tip is missing +static constexpr const char* T_set_T = "Уст:"; +static constexpr const char* T_standby = "Ожидание"; // state display in 'Standby' + +// RU-RU +static constexpr std::array dictionary = { + T_Boost, + T_Error, + T_Heating, + T_Idle, + T_NoTip, + T_set_T +}; + +} // end of namespace lang_ru_ru + diff --git a/SolderingPen_ESP32S2/lang/lang_zh_cn.h b/SolderingPen_ESP32S2/lang/lang_zh_cn.h new file mode 100644 index 0000000..0ecfa6c --- /dev/null +++ b/SolderingPen_ESP32S2/lang/lang_zh_cn.h @@ -0,0 +1,37 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once +#include +#include "i18n.h" +#include "lang_en_us.h" + +// ZH-CN +namespace lang_zh_cn { +// ZH-CN +static constexpr const char* T_Boost = "提温"; +static constexpr const char* T_Error = "错误"; +static constexpr const char* T_Idle = "禁用"; // deactivated +static constexpr const char* T_set_T = "设温:"; +static constexpr const char* T_standby = "休眠"; + +// ZH-CN +static constexpr std::array dictionary = { + T_Boost, + T_Error, + T_Idle, + lang_en_us::T_NoTip, + T_set_T, + T_standby +}; + + +} // end of namespace lang_zh_cn diff --git a/SolderingPen_ESP32S2/lang/lang_zh_tw.h b/SolderingPen_ESP32S2/lang/lang_zh_tw.h new file mode 100644 index 0000000..240a1b7 --- /dev/null +++ b/SolderingPen_ESP32S2/lang/lang_zh_tw.h @@ -0,0 +1,35 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once +#include +#include "i18n.h" +#include "lang_en_us.h" +#include "lang_zh_cn.h" + +// ZH-TW +namespace lang_zh_tw { +// ZH-TW +static constexpr const char* T_Error = "錯誤"; +static constexpr const char* T_set_T = "設溫:"; + +// ZH-CN +static constexpr std::array dictionary = { + lang_zh_cn::T_Boost, + T_Error, + lang_zh_cn::T_Idle, + lang_en_us::T_NoTip, + T_set_T, + lang_zh_cn::T_standby +}; + + +} // end of namespace lang_zh_tw diff --git a/SolderingPen_ESP32S2/log.h b/SolderingPen_ESP32S2/log.h index 2ab5e60..104d107 100644 --- a/SolderingPen_ESP32S2/log.h +++ b/SolderingPen_ESP32S2/log.h @@ -3,6 +3,12 @@ LOG macro will enable/disable logs to serial depending on LAMP_DEBUG build-time */ #pragma once +static constexpr const char* S_V = "V: "; +static constexpr const char* S_D = "D: "; +static constexpr const char* S_I = "I: "; +static constexpr const char* S_W = "W: "; +static constexpr const char* S_E = "E: "; + #ifndef PTS200_DEBUG_PORT #define PTS200_DEBUG_PORT Serial #endif @@ -28,19 +34,19 @@ LOG macro will enable/disable logs to serial depending on LAMP_DEBUG build-time #endif #if defined(PTS200_DEBUG_LEVEL) && PTS200_DEBUG_LEVEL == 5 - #define LOGV(tag, func, ...) PTS200_DEBUG_PORT.print("V: "); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) + #define LOGV(tag, func, ...) PTS200_DEBUG_PORT.print(S_V); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) #else #define LOGV(...) #endif #if defined(PTS200_DEBUG_LEVEL) && PTS200_DEBUG_LEVEL > 3 - #define LOGD(tag, func, ...) PTS200_DEBUG_PORT.print("D: "); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) + #define LOGD(tag, func, ...) PTS200_DEBUG_PORT.print(S_D); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) #else #define LOGD(...) #endif #if defined(PTS200_DEBUG_LEVEL) && PTS200_DEBUG_LEVEL > 2 - #define LOGI(tag, func, ...) PTS200_DEBUG_PORT.print("I: "); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) + #define LOGI(tag, func, ...) PTS200_DEBUG_PORT.print(S_I); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) // compat macro #define LOG(func, ...) PTS200_DEBUG_PORT.func(__VA_ARGS__) #else @@ -50,13 +56,32 @@ LOG macro will enable/disable logs to serial depending on LAMP_DEBUG build-time #endif #if defined(PTS200_DEBUG_LEVEL) && PTS200_DEBUG_LEVEL > 1 - #define LOGW(tag, func, ...) PTS200_DEBUG_PORT.print("W: "); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) + #define LOGW(tag, func, ...) PTS200_DEBUG_PORT.print(S_W); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) #else #define LOGW(...) #endif #if defined(PTS200_DEBUG_LEVEL) && PTS200_DEBUG_LEVEL > 0 - #define LOGE(tag, func, ...) PTS200_DEBUG_PORT.print("E: "); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) + #define LOGE(tag, func, ...) PTS200_DEBUG_PORT.print(S_E); PTS200_DEBUG_PORT.print(tag); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) #else #define LOGE(...) #endif + +// Per app macros +#if defined(ADC_DEBUG_LEVEL) && ADC_DEBUG_LEVEL == 5 + #define ADC_LOGV(func, ...) PTS200_DEBUG_PORT.print(S_V); PTS200_DEBUG_PORT.print(T_ADC); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) +#else + #define ADC_LOGV(...) +#endif + +#if defined(CTRL_DEBUG_LEVEL) && CTRL_DEBUG_LEVEL == 5 + #define CTRL_LOGV(func, ...) PTS200_DEBUG_PORT.print(S_V); PTS200_DEBUG_PORT.print(T_CTRL); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) +#else + #define CTRL_LOGV(...) +#endif + +#if defined(PWM_DEBUG_LEVEL) && PWM_DEBUG_LEVEL == 5 + #define PWM_LOGV(func, ...) PTS200_DEBUG_PORT.print(S_V); PTS200_DEBUG_PORT.print(T_PWM); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) +#else + #define PWM_LOGV(...) +#endif diff --git a/SolderingPen_ESP32S2/main.cpp b/SolderingPen_ESP32S2/main.cpp index 4140a4b..ef9317a 100644 --- a/SolderingPen_ESP32S2/main.cpp +++ b/SolderingPen_ESP32S2/main.cpp @@ -1,9 +1,10 @@ // #include "main.h" -#include "ts.h" +#include "ironcontroller.hpp" #include "heater.hpp" #include "sensors.hpp" -#include "Languages.h" +#include "hid.hpp" +#include "ts.h" #include "const.h" #include "log.h" @@ -11,56 +12,23 @@ #include "FirmwareMSC.h" #include "USB.h" #include "UtilsEEPROM.h" -#include // https://github.com/olikraus/u8g2 -#ifdef U8X8_HAVE_HW_SPI -#include -#endif -#ifdef U8X8_HAVE_HW_I2C -#include -#endif -// font -#include "PTS200_16.h" -// https://github.com/madhephaestus/ESP32AnalogRead -#include -#include // 用于将用户设置存储到EEPROM - - QC3Control QC(QC_DP_PIN, QC_DM_PIN); -// Setup u8g object depending on OLED controller -// 根据OLED控制器设置u8g对象 -#if defined(SSD1306) -U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE, - /* clock=*/22, /* data=*/21); -#elif defined(SH1107) -U8G2_SH1107_64X128_F_HW_I2C u8g2(U8G2_R1, SH1107_RST_PIN); -#else -#error Wrong OLED controller type! -#endif + FirmwareMSC MSC_Update; -// ADC Calibrate -ESP32AnalogRead adc_vin; // Iron tip heater object TipHeater heater(HEATER_PIN, HEATER_CHANNEL, HEATER_INVERT); // acceleration sensor GyroSensor accel; +// input voltage sensor +VinSensor vin; // Default value can be changed by user and stored in EEPROM // 用户可以更改并存储在EEPROM中的默认值 -uint16_t DefaultTemp = TEMP_DEFAULT; -uint16_t SleepTemp = TEMP_SLEEP; -uint8_t BoostTemp = TEMP_BOOST; -uint16_t time2sleep = TIME2SLEEP; -uint8_t time2off = TIME2OFF; -uint8_t timeOfBoost = TIMEOFBOOST; uint8_t MainScrType = MAINSCREEN; -//bool PIDenable = PID_ENABLE; bool beepEnable = BEEP_ENABLE; -volatile uint8_t VoltageValue = VOLTAGE_VALUE; bool QCEnable = QC_ENABLE; -uint8_t WAKEUPthreshold = WAKEUP_THRESHOLD; -bool restore_default_config = false; // Default value for T12 @@ -77,40 +45,13 @@ volatile bool ab0; // button press counters volatile int count, countMin, countMax, countStep; -// Variables for temperature control 温度控制变量 -uint16_t ShowTemp, Step; - -// Variables for voltage readings 电压读数变量 -uint16_t Vcc; -float inputVoltage; - -// State variables 状态变量 -bool inSleepMode = false; -bool inOffMode = true; -bool inBoostMode = false; -bool inCalibMode = false; -bool isWorky = true; -bool beepIfWorky = true; -bool TipIsPresent = true; -bool OledClear; - // Timing variables 时间变量 -uint32_t sleepmillis; -uint32_t boostmillis; uint32_t buttonmillis; // Buffer for drawUTF8 char F_Buffer[20]; -// accelerometer chip temperature -float lastSENSORTmp{0}; -float newSENSORTmp = 0; -uint8_t SENSORTmpTime = 0; - -// ADC Calibrate -uint16_t vref_adc0, vref_adc1; - // Language uint8_t language = 0; @@ -120,67 +61,51 @@ uint8_t hand_side = 0; // MSC Firmware bool MSC_Updating_Flag = false; -// Button2 Obj -//Button2 btn; - -uint32_t pwm_limit = 0; - -// periodic check and activate/deactivate sleep modes -Task tSleepCheck(TASK_SECOND, TASK_FOREVER, SLEEPCheck, &ts, true); - -// periodic update for the Input voltage value -Task tUpdateInputVoltage(VIN_REFRESH_INTERVAL, TASK_FOREVER, [](){ inputVoltage = getVIN();}, &ts, true ); - -// periodic checking for Iron tip presence, switch Iron modes -Task tHeaterCheck(TASK_SECOND, TASK_FOREVER, thermostatCheck, &ts, true); - // *** Arduino setup *** void setup() { - // set PD trigger pins + // set PD-trigger pins pinMode(PD_CFG_0, OUTPUT); pinMode(PD_CFG_1, OUTPUT); pinMode(PD_CFG_2, OUTPUT); // tip sensor pin pinMode(SENSOR_PIN, INPUT_PULLUP); + // buzzer in pinMode(BUZZER_PIN, OUTPUT); - // buttons pins - pinMode(BUTTON_P_PIN, INPUT_PULLUP); - pinMode(BUTTON_N_PIN, INPUT_PULLUP); - pinMode(BUTTON_PIN, INPUT_PULLUP); - - // start from default PD 5 volts + // start PD trigger with default 5 volts digitalWrite(PD_CFG_0, HIGH); Serial.begin(115200); Serial.setTxTimeoutMs(0); #if PTS200_DEBUG_LEVEL > 3 - // let ACM device intitialize + // let ACM device intitialize to catch early log output delay(3000); #endif + LOGI(T_IRON, printf, "ESPIron PTS200 firmware version: %s\n", FW_VERSION); // Start event loop task evt::start(); // event bus sniffer //evt::debug(); - // input voltage pin ADC - adc_vin.attach(VIN_PIN); - // init EEPROM 从EEPROM获取默认值 init_EEPROM(); +/* // if all 3 buttons are pressed on boot, reset EEPROM configuration to defaults + // this does not makes much sense 'cause pressed gpio0 will put MCU into fash mode if (digitalRead(BUTTON_P_PIN) == LOW && digitalRead(BUTTON_N_PIN) == LOW && digitalRead(BUTTON_PIN) == HIGH) { write_default_EEPROM(); } +*/ // load configuration from eeprom getEEPROM(); - + // fake readings for now + int VoltageValue = 20; if (QCEnable) { QC.begin(); delay(100); @@ -205,66 +130,43 @@ void setup() { } } + // Initialize IronController + espIron.init(); + // request configured voltage via PD trigger PD_Update(); - // set initial rotary encoder values 设置旋转编码器的初始值 - // a0 = PINB & 1; b0 = PIND >> 7 & 1; ab0 = (a0 == b0); - a0 = 0; - b0 = 0; - setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, DefaultTemp); - - // reset sleep timer 睡眠定时器重置 - sleepmillis = millis(); - - // long beep for setup completion 安装完成时长哔哔声 - beep(); - beep(); - - Serial.println("Soldering Pen"); - + // I2C bus (for display) Wire.begin(); Wire.setClock(100000); // 400000 - u8g2.initDisplay(); - u8g2.begin(); - u8g2.sendF("ca", 0xa8, 0x3f); - u8g2.enableUTF8Print(); - if(hand_side){ - u8g2.setDisplayRotation(U8G2_R3); - }else{ - u8g2.setDisplayRotation(U8G2_R1); - } - // initialize heater heater.init(); - // read and set saved iron temperature 读取和设置当前的烙铁头温度 - heater.setTargetTemp( DefaultTemp ); - // run accelerator sensor - accel.setMotionThreshold(WAKEUPthreshold); - accel.begin(); -} + // initialize acceleration sensor + accel.init(); -long meas_cnt, meas_ms = 0; + // intit voltage sensor + vin.init(); + + // initialize HID (buttons controls and navigation, screen) + hid.init(espIron.getTemperatures()); + + // long beep for setup completion 安装完成时长哔哔声 + beep(); + beep(); +} void loop() { - ++meas_cnt; - if (millis() - meas_ms > 1000){ - LOGV(T_DBG, printf, "lps: %u\n", meas_cnt); - meas_cnt = 0; - meas_ms = millis(); - } ts.execute(); - long timems = millis(); - ROTARYCheck(); // check rotary encoder (temp/boost setting, enter setup menu) + //ROTARYCheck(); // check rotary encoder (temp/boost setting, enter setup menu) // 检查旋转编码器(温度/升压设置,进入设置菜单) - MainScreen(); // updates the main page on the OLED 刷新OLED主界面 + //MainScreen(); // updates the main page on the OLED 刷新OLED主界面 } - +/* // check rotary encoder; set temperature, toggle boost mode, enter setup menu // accordingly 检查旋转编码器;设置温度,切换升压模式,进入设置菜单相应 void ROTARYCheck() { @@ -321,8 +223,8 @@ void ROTARYCheck() { c0 = c; // check timer when in boost mode 在升温模式时检查计时器 - if (inBoostMode && timeOfBoost) { - if ((millis() - boostmillis) / 1000 >= timeOfBoost) { + if (inBoostMode && espIron._timeout.boost) { + if ((millis() - boostmillis) / 1000 >= espIron._timeout.boost) { inBoostMode = false; // stop boost mode 停止升温模式 beep(); // beep if boost mode is over 如果升温模式结束,会发出蜂鸣声 beepIfWorky = true; // beep again when working temperature is reached @@ -330,104 +232,10 @@ void ROTARYCheck() { } } } - -// check and activate/deactivate sleep modes 检查和激活/关闭睡眠模式 -void SLEEPCheck() { - if (inOffMode) - return; - - if (accel.motionDetect()) { // if handle was moved 如果手柄被移动 - if (inSleepMode) { // in sleep or off mode? 在睡眠模式还是关机模式? -/* - u8g2.setPowerSave(0); - pwm_limit = POWER_LIMIT_20; - if (VoltageValue < 3) { - pwm_limit = POWER_LIMIT_15; - } - heater.enable(); */ - beep(); // beep on wake-up - beepIfWorky = true; // beep again when working temperature is reached - // 当达到工作温度,会发出蜂鸣声 - inSleepMode = false; // reset sleep flag - } - //handleMoved = false; // reset handleMoved flag - // inOffMode = false; // reset off flag - // reset sleep timer - sleepmillis = millis(); - } - // check time passed since the handle was moved 检查把手被移动后经过的时间 - auto motionlesstime = (millis() - sleepmillis) / 1000; - if ((!inSleepMode) && (time2sleep > 0) && (motionlesstime >= time2sleep)) { - LOGI(T_DBG, printf, "Switch to sleep temp:%u due to sleep timeout of %u sec\n", SleepTemp, time2sleep); - inSleepMode = true; - beep(); - } else if ((!inOffMode) && (time2off > 0) && - ((motionlesstime / 60) >= time2off)) { - inSleepMode = false; - inOffMode = true; - //u8g2.setPowerSave(1); - beep(); - LOGI(T_DBG, println, "Switch-Off due to idle timeout"); - } -} - - - -// calculates real temperature value according to ADC reading and calibration -// values 根据ADC读数和校准值,计算出真实的温度值 -int32_t calculateTemp(float t) { -// if (RawTemp < 200) -// CurrentTemp = map(RawTemp, 20, 200, 20, CalTemp[CurrentTip][0]); - if (t < 200) - return map(static_cast(t), 20, 200, 20, CalTemp[CurrentTip][0]); - - if (t < 280) - return map(static_cast(t), 200, 280, CalTemp[CurrentTip][0], CalTemp[CurrentTip][1]); - - return map(static_cast(t), 280, 360, CalTemp[CurrentTip][1], CalTemp[CurrentTip][2]); -} - -// controls the heater 控制加热器 -void thermostatCheck() { - // define heater TargetTemp according to current working mode / 根据当前工作模式定义设定值 - if (inOffMode) - heater.disable(); - else if (inSleepMode) - heater.setTargetTemp(SleepTemp); - else if (inBoostMode) { - heater.setTargetTemp( constrain(heater.getTargetTemp() + BoostTemp, 0, TEMP_MAX) ); - } - - // refresh displayed temp value - ShowTemp = heater.getCurrentTemp(); - - // set state variable if temperature is in working range; beep if working temperature has been just reached - // 温度在工作范围内可设置状态变量;当工作温度刚刚达到时,会发出蜂鸣声 - int gap = abs(heater.getTargetTemp() - heater.getCurrentTemp()); - if (gap < 5) { - if (!isWorky && beepIfWorky) beep(); - isWorky = true; - beepIfWorky = false; - } else - isWorky = false; - - // checks if tip is present or currently inserted - // 检查烙铁头是否存在或当前已插入 - if (ShowTemp > TEMP_NOTIP) TipIsPresent = false; // tip removed ? 烙铁头移除? - if (!TipIsPresent && (ShowTemp < TEMP_NOTIP)) { // new tip inserted ? 新的烙铁头插入? - beep(); // beep for info - TipIsPresent = true; // tip is present now 烙铁头已经存在 - ChangeTipScreen(); // show tip selection screen 显示烙铁头选择屏幕 - update_EEPROM(); // update setting in EEPROM EEPROM的更新设置 - //handleMoved = true; // reset all timers 重置所有计时器 - c0 = LOW; // switch must be released 必须松开开关 - setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, heater.getTargetTemp()); // reset rotary encoder 重置旋转编码器 - } -} // creates a short beep on the buzzer 在蜂鸣器上创建一个短的哔哔声 void beep() { @@ -441,661 +249,11 @@ void beep() { } } -// 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 = rstep; - count = rvalue << ROTARY_TYPE; -} - -// reads current rotary encoder value 读取当前旋转编码器值 -int getRotary() { - Button_loop(); - return (count >> ROTARY_TYPE); -} - // reads user settings from EEPROM; if EEPROM values are invalid, write defaults // 从EEPROM读取用户设置;如果EEPROM值无效,则写入默认值 void getEEPROM() { read_EEPROM(); } -// draws the main screen 绘制主屏幕 -void MainScreen() { - u8g2.firstPage(); - do { - // u8g2.setCursor(0, 0); - // u8g2.print(F("nihao")); - // draw heater.getTargetTemp() temperature - u8g2.setFont(PTS200_16); - if(language == 2){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } - u8g2.setFontPosTop(); - // u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, "设温:"); - u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, txt_set_temp[language]); - u8g2.setCursor(40, 0 + SCREEN_OFFSET); - u8g2.setFont(u8g2_font_unifont_t_chinese3); - u8g2.print(heater.getTargetTemp(), 0); - - u8g2.setFont(PTS200_16); - if(language == 2){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } - // draw status of heater 绘制加热器状态 - u8g2.setCursor(96, 0 + SCREEN_OFFSET); - if (ShowTemp > TEMP_NOTIP) - u8g2.print(txt_error[language]); - else if (inOffMode) - u8g2.print(txt_off[language]); - else if (inSleepMode) - u8g2.print(txt_sleep[language]); - else if (inBoostMode) - u8g2.print(txt_boost[language]); - else if (isWorky) - u8g2.print(txt_worky[language]); - else if (heater.getCurrentTemp() - heater.getTargetTemp() < PID_ENGAGE_DIFF) - u8g2.print(txt_on[language]); - else - u8g2.print(txt_hold[language]); - - u8g2.setFont(u8g2_font_unifont_t_chinese3); - // rest depending on main screen type 休息取决于主屏幕类型 - if (MainScrType) { - // draw current tip and input voltage 绘制当前烙铁头及输入电压 - newSENSORTmp = newSENSORTmp + 0.01 * accel.getAccellTemp(); - SENSORTmpTime++; - if (SENSORTmpTime >= 100) { - lastSENSORTmp = newSENSORTmp; - newSENSORTmp = 0; - SENSORTmpTime = 0; - } - u8g2.setCursor(0, 50); - u8g2.print(lastSENSORTmp, 1); - u8g2.print(F("C")); - u8g2.setCursor(83, 50); - u8g2.print(inputVoltage/1000, 1); // convert mv in V - u8g2.print(F("V")); - // draw current temperature 绘制当前温度 - u8g2.setFont(u8g2_font_freedoomr25_tn); - u8g2.setFontPosTop(); - u8g2.setCursor(37, 18); - if (ShowTemp > 500) - u8g2.print("Err"); - else - u8g2.printf("%03d", ShowTemp); - } else { - // draw current temperature in big figures 用大数字绘制当前温度 - u8g2.setFont(u8g2_font_fub42_tn); - u8g2.setFontPosTop(); - u8g2.setCursor(15, 20); - if (ShowTemp > 500) - u8g2.print("Err"); - else - u8g2.printf("%03d", ShowTemp); - } - } while (u8g2.nextPage()); -} - -// setup screen 设置屏幕 -void SetupScreen() { - heater.disable(); - beep(); - 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 3: { - MainScrType = - MenuScreen(MainScreenItems, sizeof(MainScreenItems), MainScrType); - } break; - case 4: { - InfoScreen(); - } break; - case 5: - VoltageValue = - MenuScreen(VoltageItems, sizeof(VoltageItems), VoltageValue); - PD_Update(); - break; - case 6: - QCEnable = MenuScreen(QCItems, sizeof(QCItems), QCEnable); - break; - case 7: - beepEnable = MenuScreen(BuzzerItems, sizeof(BuzzerItems), beepEnable); - break; - case 8: { - restore_default_config = MenuScreen(DefaultItems, sizeof(DefaultItems), - restore_default_config); - if (restore_default_config) { - restore_default_config = false; - write_default_EEPROM(); - read_EEPROM(); - } - } break; - case 9: { - bool lastbutton = (!digitalRead(BUTTON_PIN)); - u8g2.clearBuffer(); // clear the internal memory - u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font - u8g2.drawStr(0, 10, - "MSC Update"); // write something to the internal memory - u8g2.sendBuffer(); // transfer internal memory to the display - delay(1000); - do { - MSC_Update.onEvent(usbEventCallback); - MSC_Update.begin(); - if (lastbutton && digitalRead(BUTTON_PIN)) { - delay(10); - lastbutton = false; - } - } while (digitalRead(BUTTON_PIN) || lastbutton); - - MSC_Update.end(); - } break; - case 10: { - Serial.println(language); - language = MenuScreen(LanguagesItems, sizeof(LanguagesItems), language); - Serial.println(language); - repeat = false; - } break; - case 11: { - if(hand_side == 0){ - u8g2.setDisplayRotation(U8G2_R3); - hand_side = 1; - }else{ - u8g2.setDisplayRotation(U8G2_R1); - hand_side = 0; - } - repeat = false; - } break; - default: - repeat = false; - break; - } - } - update_EEPROM(); - heater.enable(); - setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, heater.getTargetTemp()); -} - -// 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(50, TEMP_MAX, 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, 600, 10, time2sleep); - time2sleep = InputScreen(SleepTimerItems); - break; - case 1: - setRotary(0, 60, 1, time2off); - time2off = InputScreen(OffTimerItems); - break; - case 2: - setRotary(0, 180, 10, timeOfBoost); - timeOfBoost = InputScreen(BoostTimerItems); - break; - case 3: - setRotary(0, 50, 5, WAKEUPthreshold); - WAKEUPthreshold = InputScreen(WAKEUPthresholdItems); - accel.setMotionThreshold(WAKEUPthreshold); - break; - default: - repeat = false; - break; - } - } -} - -// menu screen 菜单屏幕 -uint8_t MenuScreen(const char *Items[][language_types], uint8_t numberOfItems, - uint8_t selected) { - // Serial.println(numberOfItems); - bool isTipScreen = ((strcmp(Items[0][language], "烙铁头:") == 0) || - (strcmp(Items[0][language], "Tip:") == 0) || - (strcmp(Items[0][language], "烙鐵頭:") == 0)); - uint8_t lastselected = selected; - int8_t arrow = 0; - if (selected) arrow = 1; - numberOfItems = numberOfItems / language_types; - numberOfItems >>= 2; - - // 根据OLED控制器设置选择方向 -#if defined(SSD1306) - setRotary(0, numberOfItems + 3, 1, selected); -#elif defined(SH1107) - setRotary(0, numberOfItems - 2, 1, selected); -#else -#error Wrong OLED controller type! -#endif - - bool lastbutton = (!digitalRead(BUTTON_PIN)); - - do { - selected = getRotary(); - arrow = constrain(arrow + selected - lastselected, 0, 2); - lastselected = selected; - u8g2.firstPage(); - do { - u8g2.setFont(PTS200_16); - if(language == 2){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } - u8g2.setFontPosTop(); - u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, Items[0][language]); - if (isTipScreen) - u8g2.drawUTF8(54, 0 + SCREEN_OFFSET, TipName[CurrentTip]); - u8g2.drawUTF8(0, 16 * (arrow + 1) + SCREEN_OFFSET, ">"); - for (uint8_t i = 0; i < 3; i++) { - uint8_t drawnumber = selected + i + 1 - arrow; - if (drawnumber < numberOfItems) - u8g2.drawUTF8(12, 16 * (i + 1) + SCREEN_OFFSET, - Items[selected + i + 1 - arrow][language]); - } - } while (u8g2.nextPage()); - if (lastbutton && digitalRead(BUTTON_PIN)) { - delay(10); - lastbutton = false; - } - } while (digitalRead(BUTTON_PIN) || lastbutton); - - beep(); - return selected; -} - -void MessageScreen(const char *Items[][language_types], uint8_t numberOfItems) { - numberOfItems = numberOfItems / language_types; - bool lastbutton = (!digitalRead(BUTTON_PIN)); - u8g2.firstPage(); - do { - u8g2.setFont(PTS200_16); - if(language == 2){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } - u8g2.setFontPosTop(); - for (uint8_t i = 0; i < numberOfItems; i++) - u8g2.drawUTF8(0, i * 16, Items[i][language]); - } while (u8g2.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[][language_types]) { - uint16_t value; - bool lastbutton = (!digitalRead(BUTTON_PIN)); - - do { - value = getRotary(); - u8g2.firstPage(); - do { - u8g2.setFont(PTS200_16); - if(language == 2){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } - u8g2.setFontPosTop(); - u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, Items[0][language]); - u8g2.setCursor(0, 32); - u8g2.print(">"); - u8g2.setCursor(10, 32); - if (value == 0) - u8g2.print(txt_Deactivated[language]); - else { - u8g2.print(value); - u8g2.print(" "); - u8g2.print(Items[1][language]); - } - } while (u8g2.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 { - float fTmp = accel.getAccellTemp(); // read cold junction temperature - u8g2.firstPage(); - do { - u8g2.setFont(PTS200_16); - if(language == 2){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } - u8g2.setFontPosTop(); - u8g2.setCursor(0, 0 + SCREEN_OFFSET); - u8g2.print(txt_temp[language]); - u8g2.print(fTmp, 1); - u8g2.print(F(" C")); - u8g2.setCursor(0, 16 + SCREEN_OFFSET); - u8g2.print(txt_voltage[language]); - u8g2.print(getVIN()/1000, 1); // convert mv in V - u8g2.print(F(" V")); - u8g2.setCursor(0, 16 * 2 + SCREEN_OFFSET); - u8g2.print(txt_Version[language]); - u8g2.print(VERSION); - // u8g2.setCursor(0, 48); u8g2.print(F("IMU: ")); - // u8g2.print(accelerometer[1], DEC); u8g2.print(F("")); - } while (u8g2.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)); - - Serial.print("selected: "); - Serial.println(selected); - Serial.print("lastselected: \n"); - Serial.println(lastselected); - Serial.print("NumberOfTips: \n"); - Serial.println(NumberOfTips); - - do { - selected = getRotary(); - arrow = constrain(arrow + selected - lastselected, 0, 2); - lastselected = selected; - u8g2.firstPage(); - do { - u8g2.setFont(PTS200_16); - if(language == 2){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } - u8g2.setFontPosTop(); - // strcpy_P(F_Buffer, PSTR("选择烙铁头")); - u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, txt_select_tip[language]); - u8g2.drawUTF8(0, 16 * (arrow + 1) + SCREEN_OFFSET, ">"); - for (uint8_t i = 0; i < 3; i++) { - uint8_t drawnumber = selected + i - arrow; - if (drawnumber < NumberOfTips) - u8g2.drawUTF8(12, 16 * (i + 1) + SCREEN_OFFSET, - TipName[selected + i - arrow]); - } - } while (u8g2.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]; - uint16_t lastTargetTemp = heater.getTargetTemp(); - for (uint8_t CalStep = 0; CalStep < 3; CalStep++) { - heater.setTargetTemp( CalTemp[CurrentTip][CalStep] ); - LOGI(T_CTRL, print, "Target Temp: "); - LOGI(T_CTRL, println, heater.getTargetTemp()); - setRotary(100, 500, 1, heater.getTargetTemp()); - beepIfWorky = true; - bool lastbutton = (!digitalRead(BUTTON_PIN)); - - do { - thermostatCheck(); // heater control - - u8g2.firstPage(); - do { - u8g2.setFont(PTS200_16); - if(language == 2){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } - u8g2.setFontPosTop(); - // strcpy_P(F_Buffer, PSTR("校准")); - u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, txt_calibrate[language]); - u8g2.setCursor(0, 16 + SCREEN_OFFSET); - u8g2.print(txt_step[language]); - u8g2.print(CalStep + 1); - u8g2.print(" of 3"); - if (isWorky) { - u8g2.setCursor(0, 32 + SCREEN_OFFSET); - u8g2.print(txt_set_measured[language]); - u8g2.setCursor(0, 48 + SCREEN_OFFSET); - u8g2.print(txt_s_temp[language]); - u8g2.print(getRotary()); - } else { - u8g2.setCursor(0, 32 + SCREEN_OFFSET); - u8g2.print(txt_temp_2[language]); - u8g2.print(heater.getCurrentTemp()); - u8g2.setCursor(0, 48 + SCREEN_OFFSET); - u8g2.print(txt_wait_pls[language]); - } - } while (u8g2.nextPage()); - if (lastbutton && digitalRead(BUTTON_PIN)) { - delay(10); - lastbutton = false; - } - } while (digitalRead(BUTTON_PIN) || lastbutton); - - CalTempNew[CalStep] = getRotary(); - beep(); - delay(10); - } - - heater.disable(); // shut off heater 关闭加热器 - if (VoltageValue == 3) { - delayMicroseconds(TIME2SETTLE_20V); - } else { - delayMicroseconds(TIME2SETTLE); // wait for voltage to settle 等待电压稳定 - } - CalTempNew[3] = accel.getAccellTemp(); // 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]; - } - } - - heater.setTargetTemp( lastTargetTemp ); - update_EEPROM(); -} - -// 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); - } - u8g2.firstPage(); - do { - u8g2.setFont(PTS200_16); - if(language == 2){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } - u8g2.setFontPosTop(); - u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, txt_enter_tip_name[language]); - u8g2.setCursor(12 * digit, 48 + SCREEN_OFFSET); - u8g2.print(char(94)); - u8g2.setCursor(0, 32 + SCREEN_OFFSET); - for (uint8_t i = 0; i < digit; i++) u8g2.print(TipName[CurrentTip][i]); - u8g2.setCursor(12 * digit, 32 + SCREEN_OFFSET); - u8g2.print(char(value)); - } while (u8g2.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)); -} - -// get supply voltage in mV 得到以mV为单位的电源电压 -float getVIN() { - uint32_t voltage = 0; - - for (uint8_t i = 0; i < 4; i++) { // get 32 readings 得到32个读数 - voltage += adc_vin.readMiliVolts(); - } - return voltage / 4 * 31.3f; - - // some calibration calc - // // VIN_Ru = 100k, Rd_GND = 3.3K - // if (value < 500) - // { - // voltage = value * 1390 * 31.3 / 4095 * 1.35; - // } - // else if (500 <= value && value < 1000) - // { - // voltage = value * 1390 * 31.3 / 4095 * 1.135; - // } - // else if (1000 <= value && value < 1500) - // { - // voltage = value * 1390 * 31.3 / 4095 * 1.071; - // } - // else if (1500 <= value && value < 2000) - // { - // voltage = value * 1390 * 31.3 / 4095; - // } - // else if (2000 <= value && value < 3000) - // { - // voltage = value * 1390 * 31.3 / 4095; - // } - // else - // voltage = value * 1390 * 31.3 / 4095; -} int32_t variance(int16_t a[]) { // Compute mean (average of elements)计算平均值(元素的平均值) @@ -1109,59 +267,9 @@ int32_t variance(int16_t a[]) { return (int32_t)sqDiff / 32; } -unsigned int Button_Time1 = 0, Button_Time2 = 0; - -void Button_loop() { - // '-' decrementing button - if (!digitalRead(BUTTON_N_PIN) && a0 == 1) { - delay(BUTTON_DELAY); - if (!digitalRead(BUTTON_N_PIN)) { - int count0 = count; - count = constrain(count + countStep, countMin, countMax); - if (!(countMin == TEMP_MIN && countMax == TEMP_MAX)) { - if (count0 + countStep > countMax) { - count = countMin; - } - } - a0 = 0; - } - } else if (!digitalRead(BUTTON_N_PIN) && a0 == 0) { - delay(BUTTON_DELAY); - if (Button_Time1 > 10) // this value controls long-press timeout / 这里的数越大,需要长按时间更长 - count = constrain(count + countStep, countMin, countMax); - else - Button_Time1++; - } else if (digitalRead(BUTTON_N_PIN)) { - Button_Time1 = 0; - a0 = 1; - } - - // '+' incrementing button - if (!digitalRead(BUTTON_P_PIN) && b0 == 1) { - delay(BUTTON_DELAY); - if (!digitalRead(BUTTON_P_PIN)) { - int count0 = count; - count = constrain(count - countStep, countMin, countMax); - if (!(countMin == TEMP_MIN && countMax == TEMP_MAX)) { - if (count0 - countStep < countMin) { - count = countMax; - } - } - b0 = 0; - } - } else if (!digitalRead(BUTTON_P_PIN) && b0 == 0) { - delay(BUTTON_DELAY); - if (Button_Time2 > 10) // this value controls long-press timeout / 这里的数越大,需要长按时间更长 - count = constrain(count - countStep, countMin, countMax); - else - Button_Time2++; - } else if (digitalRead(BUTTON_P_PIN)) { - Button_Time2 = 0; - b0 = 1; - } -} - void PD_Update() { + // fake readings for now + int VoltageValue = 20; switch (VoltageValue) { // 9 volts case 0: { @@ -1201,6 +309,8 @@ void PD_Update() { static void usbEventCallback(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + // disable it for now +/* if (event_base == ARDUINO_USB_EVENTS) { // arduino_usb_event_data_t* data = (arduino_usb_event_data_t*)event_data; switch (event_id) { @@ -1298,24 +408,6 @@ static void usbEventCallback(void *arg, esp_event_base_t event_base, break; } } +*/ } -// uint16_t calibrate_adc(adc_unit_t adc, adc_atten_t channel) { -// uint16_t vref; -// esp_adc_cal_characteristics_t adc_chars; -// esp_adc_cal_value_t val_type = esp_adc_cal_characterize((adc_unit_t)adc, -// (adc_atten_t)channel, (adc_bits_width_t)ADC_WIDTH_BIT_12, 1100, -// &adc_chars); -// //Check type of calibration value used to characterize ADC -// if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { -// Serial.printf("eFuse Vref:%u mV", adc_chars.vref); -// Serial.println(); -// vref = adc_chars.vref; -// } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { -// Serial.printf("Two Point --> coeff_a:%umV coeff_b:%umV\n", -// adc_chars.coeff_a, adc_chars.coeff_b); Serial.println(); -// } else { -// Serial.println("Default Vref: 1100mV"); -// } -// return vref; -// } diff --git a/SolderingPen_ESP32S2/nvs.cpp b/SolderingPen_ESP32S2/nvs.cpp new file mode 100644 index 0000000..f85140e --- /dev/null +++ b/SolderingPen_ESP32S2/nvs.cpp @@ -0,0 +1,49 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ + +#include "Arduino.h" +#include "nvs.hpp" +#include "log.h" + +static constexpr const char* T_NVS = "NVS"; + +esp_err_t nvs_blob_read(const char* nvsspace, const char* key, void* blob, size_t len){ + esp_err_t err; + std::unique_ptr nvs = nvs::open_nvs_handle(nvsspace, NVS_READONLY, &err); + + if (err != ESP_OK) { + LOGD(T_NVS, printf, "Err opening NVS namespace:%s RO, err:%s\n", nvsspace, esp_err_to_name(err)); + return err; + } + + err = nvs->get_blob(key, blob, len); + if (err != ESP_OK) { + LOGD(T_NVS, printf, "Err reading NVS blob namespace:%s, key:%s, err:%s\n", nvsspace, key, esp_err_to_name(err)); + } + return err; +} + +esp_err_t nvs_blob_write(const char* nvsspace, const char* key, void* blob, size_t len){ + esp_err_t err; + std::unique_ptr nvs = nvs::open_nvs_handle(nvsspace, NVS_READWRITE, &err); + + if (err != ESP_OK) { + LOGD(T_NVS, printf, "Err opening NVS namespace:%s RW, err:%s\n", nvsspace, esp_err_to_name(err)); + return err; + } + + err = nvs->set_blob(key, blob, len); + if (err != ESP_OK) { + LOGD(T_NVS, printf, "Err writing NVS namespace:%s, key:%s, err:%s\n", nvsspace, key, esp_err_to_name(err)); + } + return err; +} diff --git a/SolderingPen_ESP32S2/nvs.hpp b/SolderingPen_ESP32S2/nvs.hpp new file mode 100644 index 0000000..3c1d85c --- /dev/null +++ b/SolderingPen_ESP32S2/nvs.hpp @@ -0,0 +1,21 @@ +/* + This file is a part of ESPIron-PTS200 project + https://github.com/vortigont/ESPIron-PTS200 + + Copyright © 2024 Emil Muratov (vortigont) + + ESPIron-PTS200 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#pragma once +#include "nvs_handle.hpp" +#include "common.hpp" + +// little helpers for reading/writing to NVS + +esp_err_t nvs_blob_read(const char* nvsspace, const char* key, void* blob, size_t len); + +esp_err_t nvs_blob_write(const char* nvsspace, const char* key, void* blob, size_t len); + diff --git a/SolderingPen_ESP32S2/oldcode.cpp b/SolderingPen_ESP32S2/oldcode.cpp new file mode 100644 index 0000000..b25c300 --- /dev/null +++ b/SolderingPen_ESP32S2/oldcode.cpp @@ -0,0 +1,736 @@ +#ifdef DO_NOT_COMPILE + +/* +// controls the heater 控制加热器 +void thermostatCheck() { + // define heater TargetTemp according to current working mode / 根据当前工作模式定义设定值 + if (inOffMode) + heater.disable(); + else if (inSleepMode) + heater.setTargetTemp(espIron._temp.sleep); + else if (inBoostMode) { + heater.setTargetTemp( constrain(heater.getTargetTemp() + espIron._temp.boost, 0, TEMP_MAX) ); + } + + // refresh displayed temp value + ShowTemp = heater.getCurrentTemp(); + + // set state variable if temperature is in working range; beep if working temperature has been just reached + // 温度在工作范围内可设置状态变量;当工作温度刚刚达到时,会发出蜂鸣声 + int gap = abs(heater.getTargetTemp() - heater.getCurrentTemp()); + if (gap < 5) { + if (!isWorky && beepIfWorky) beep(); + isWorky = true; + beepIfWorky = false; + } else + isWorky = false; + + // checks if tip is present or currently inserted + // 检查烙铁头是否存在或当前已插入 + if (ShowTemp > TEMP_NOTIP) TipIsPresent = false; // tip removed ? 烙铁头移除? + if (!TipIsPresent && (ShowTemp < TEMP_NOTIP)) { // new tip inserted ? 新的烙铁头插入? + beep(); // beep for info + TipIsPresent = true; // tip is present now 烙铁头已经存在 + ChangeTipScreen(); // show tip selection screen 显示烙铁头选择屏幕 + update_EEPROM(); // update setting in EEPROM EEPROM的更新设置 + //handleMoved = true; // reset all timers 重置所有计时器 + c0 = LOW; // switch must be released 必须松开开关 + setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, heater.getTargetTemp()); // reset rotary encoder 重置旋转编码器 + } +} +*/ + +// 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 = rstep; + count = rvalue << ROTARY_TYPE; +} + +// reads current rotary encoder value 读取当前旋转编码器值 +int getRotary() { + Button_loop(); + return (count >> ROTARY_TYPE); +} + + +// draws the main screen 绘制主屏幕 +void MainScreen() { + u8g2.firstPage(); + do { + // u8g2.setCursor(0, 0); + // u8g2.print(F("nihao")); + // draw heater.getTargetTemp() temperature + u8g2.setFont(PTS200_16); + if(language == 2){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } + u8g2.setFontPosTop(); + // u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, "设温:"); + u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, txt_set_temp[language]); + u8g2.setCursor(40, 0 + SCREEN_OFFSET); + u8g2.setFont(u8g2_font_unifont_t_chinese3); + u8g2.print(heater.getTargetTemp(), 0); + + u8g2.setFont(PTS200_16); + if(language == 2){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } + // draw status of heater 绘制加热器状态 + u8g2.setCursor(96, 0 + SCREEN_OFFSET); + if (ShowTemp > TEMP_NOTIP) + u8g2.print(txt_error[language]); + else if (inOffMode) + u8g2.print(txt_off[language]); + else if (inSleepMode) + u8g2.print(txt_sleep[language]); + else if (inBoostMode) + u8g2.print(txt_boost[language]); + else if (isWorky) + u8g2.print(txt_worky[language]); + else if (heater.getCurrentTemp() - heater.getTargetTemp() < PID_ENGAGE_DIFF) + u8g2.print(txt_on[language]); + else + u8g2.print(txt_hold[language]); + + u8g2.setFont(u8g2_font_unifont_t_chinese3); + // rest depending on main screen type 休息取决于主屏幕类型 + if (MainScrType) { + // draw current tip and input voltage 绘制当前烙铁头及输入电压 + newSENSORTmp = newSENSORTmp + 0.01 * accel.getAccellTemp(); + SENSORTmpTime++; + if (SENSORTmpTime >= 100) { + lastSENSORTmp = newSENSORTmp; + newSENSORTmp = 0; + SENSORTmpTime = 0; + } + u8g2.setCursor(0, 50); + u8g2.print(lastSENSORTmp, 1); + u8g2.print(F("C")); + u8g2.setCursor(83, 50); + u8g2.print(inputVoltage/1000, 1); // convert mv in V + u8g2.print(F("V")); + // draw current temperature 绘制当前温度 + u8g2.setFont(u8g2_font_freedoomr25_tn); + u8g2.setFontPosTop(); + u8g2.setCursor(37, 18); + if (ShowTemp > 500) + u8g2.print("Err"); + else + u8g2.printf("%03d", ShowTemp); + } else { + // draw current temperature in big figures 用大数字绘制当前温度 + u8g2.setFont(u8g2_font_fub42_tn); + u8g2.setFontPosTop(); + u8g2.setCursor(15, 20); + if (ShowTemp > 500) + u8g2.print("Err"); + else + u8g2.printf("%03d", ShowTemp); + } + } while (u8g2.nextPage()); +} + +// setup screen 设置屏幕 +void SetupScreen() { + heater.disable(); + beep(); + 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 3: { + MainScrType = + MenuScreen(MainScreenItems, sizeof(MainScreenItems), MainScrType); + } break; + case 4: { + InfoScreen(); + } break; + case 5: + VoltageValue = + MenuScreen(VoltageItems, sizeof(VoltageItems), VoltageValue); + PD_Update(); + break; + case 6: + QCEnable = MenuScreen(QCItems, sizeof(QCItems), QCEnable); + break; + case 7: + beepEnable = MenuScreen(BuzzerItems, sizeof(BuzzerItems), beepEnable); + break; + case 8: { + restore_default_config = MenuScreen(DefaultItems, sizeof(DefaultItems), + restore_default_config); + if (restore_default_config) { + restore_default_config = false; + write_default_EEPROM(); + read_EEPROM(); + } + } break; + case 9: { + bool lastbutton = (!digitalRead(BUTTON_PIN)); + u8g2.clearBuffer(); // clear the internal memory + u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font + u8g2.drawStr(0, 10, + "MSC Update"); // write something to the internal memory + u8g2.sendBuffer(); // transfer internal memory to the display + delay(1000); + do { + MSC_Update.onEvent(usbEventCallback); + MSC_Update.begin(); + if (lastbutton && digitalRead(BUTTON_PIN)) { + delay(10); + lastbutton = false; + } + } while (digitalRead(BUTTON_PIN) || lastbutton); + + MSC_Update.end(); + } break; + case 10: { + Serial.println(language); + language = MenuScreen(LanguagesItems, sizeof(LanguagesItems), language); + Serial.println(language); + repeat = false; + } break; + case 11: { + if(hand_side == 0){ + u8g2.setDisplayRotation(U8G2_R3); + hand_side = 1; + }else{ + u8g2.setDisplayRotation(U8G2_R1); + hand_side = 0; + } + repeat = false; + } break; + default: + repeat = false; + break; + } + } + update_EEPROM(); + heater.enable(); + setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, heater.getTargetTemp()); +} + +// 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, espIron._temp.working); + espIron.setTempWorking(InputScreen(DefaultTempItems)); + break; + case 1: + setRotary(50, TEMP_MAX, TEMP_STEP, espIron._temp.sleep); + espIron.setTempSleep( InputScreen(SleepTempItems) ); + break; + case 2: + setRotary(10, 100, TEMP_STEP, espIron._temp.boost); + espIron.setTempBoost( 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, 600, 10, espIron._timeout.sleep); + espIron.setTimeOutSleep( InputScreen(SleepTimerItems) ); + break; + case 1: + setRotary(0, 60, 1, espIron._timeout.off); + espIron.setTimeOutOff( InputScreen(OffTimerItems) ); + break; + case 2: + setRotary(0, 180, 10, espIron._timeout.boost); + espIron.setTimeOutBoost( InputScreen(BoostTimerItems) ); + break; + case 3: { + // todo: get this from NVS + setRotary(0, 50, 5, WAKEUPthreshold); + WAKEUPthreshold = InputScreen(WAKEUPthresholdItems); + accel.setMotionThreshold(WAKEUPthreshold); + break; + } + default: + repeat = false; + break; + } + } +} + +// menu screen 菜单屏幕 +uint8_t MenuScreen(const char *Items[][language_types], uint8_t numberOfItems, + uint8_t selected) { + // Serial.println(numberOfItems); + bool isTipScreen = ((strcmp(Items[0][language], "烙铁头:") == 0) || + (strcmp(Items[0][language], "Tip:") == 0) || + (strcmp(Items[0][language], "烙鐵頭:") == 0)); + uint8_t lastselected = selected; + int8_t arrow = 0; + if (selected) arrow = 1; + numberOfItems = numberOfItems / language_types; + numberOfItems >>= 2; + + // 根据OLED控制器设置选择方向 +#if defined(SSD1306) + setRotary(0, numberOfItems + 3, 1, selected); +#elif defined(SH1107) + setRotary(0, numberOfItems - 2, 1, selected); +#else +#error Wrong OLED controller type! +#endif + + bool lastbutton = (!digitalRead(BUTTON_PIN)); + + do { + selected = getRotary(); + arrow = constrain(arrow + selected - lastselected, 0, 2); + lastselected = selected; + u8g2.firstPage(); + do { + u8g2.setFont(PTS200_16); + if(language == 2){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } + u8g2.setFontPosTop(); + u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, Items[0][language]); + if (isTipScreen) + u8g2.drawUTF8(54, 0 + SCREEN_OFFSET, TipName[CurrentTip]); + u8g2.drawUTF8(0, 16 * (arrow + 1) + SCREEN_OFFSET, ">"); + for (uint8_t i = 0; i < 3; i++) { + uint8_t drawnumber = selected + i + 1 - arrow; + if (drawnumber < numberOfItems) + u8g2.drawUTF8(12, 16 * (i + 1) + SCREEN_OFFSET, + Items[selected + i + 1 - arrow][language]); + } + } while (u8g2.nextPage()); + if (lastbutton && digitalRead(BUTTON_PIN)) { + delay(10); + lastbutton = false; + } + } while (digitalRead(BUTTON_PIN) || lastbutton); + + beep(); + return selected; +} + +void MessageScreen(const char *Items[][language_types], uint8_t numberOfItems) { + numberOfItems = numberOfItems / language_types; + bool lastbutton = (!digitalRead(BUTTON_PIN)); + u8g2.firstPage(); + do { + u8g2.setFont(PTS200_16); + if(language == 2){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } + u8g2.setFontPosTop(); + for (uint8_t i = 0; i < numberOfItems; i++) + u8g2.drawUTF8(0, i * 16, Items[i][language]); + } while (u8g2.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[][language_types]) { + uint16_t value; + bool lastbutton = (!digitalRead(BUTTON_PIN)); + + do { + value = getRotary(); + u8g2.firstPage(); + do { + u8g2.setFont(PTS200_16); + if(language == 2){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } + u8g2.setFontPosTop(); + u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, Items[0][language]); + u8g2.setCursor(0, 32); + u8g2.print(">"); + u8g2.setCursor(10, 32); + if (value == 0) + u8g2.print(txt_Deactivated[language]); + else { + u8g2.print(value); + u8g2.print(" "); + u8g2.print(Items[1][language]); + } + } while (u8g2.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 { + float fTmp = accel.getAccellTemp(); // read cold junction temperature + u8g2.firstPage(); + do { + u8g2.setFont(PTS200_16); + if(language == 2){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } + u8g2.setFontPosTop(); + u8g2.setCursor(0, 0 + SCREEN_OFFSET); + u8g2.print(txt_temp[language]); + u8g2.print(fTmp, 1); + u8g2.print(F(" C")); + u8g2.setCursor(0, 16 + SCREEN_OFFSET); + u8g2.print(txt_voltage[language]); + u8g2.print(getVIN()/1000, 1); // convert mv in V + u8g2.print(F(" V")); + u8g2.setCursor(0, 16 * 2 + SCREEN_OFFSET); + u8g2.print(txt_Version[language]); + u8g2.print(VERSION); + // u8g2.setCursor(0, 48); u8g2.print(F("IMU: ")); + // u8g2.print(accelerometer[1], DEC); u8g2.print(F("")); + } while (u8g2.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)); + + Serial.print("selected: "); + Serial.println(selected); + Serial.print("lastselected: \n"); + Serial.println(lastselected); + Serial.print("NumberOfTips: \n"); + Serial.println(NumberOfTips); + + do { + selected = getRotary(); + arrow = constrain(arrow + selected - lastselected, 0, 2); + lastselected = selected; + u8g2.firstPage(); + do { + u8g2.setFont(PTS200_16); + if(language == 2){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } + u8g2.setFontPosTop(); + // strcpy_P(F_Buffer, PSTR("选择烙铁头")); + u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, txt_select_tip[language]); + u8g2.drawUTF8(0, 16 * (arrow + 1) + SCREEN_OFFSET, ">"); + for (uint8_t i = 0; i < 3; i++) { + uint8_t drawnumber = selected + i - arrow; + if (drawnumber < NumberOfTips) + u8g2.drawUTF8(12, 16 * (i + 1) + SCREEN_OFFSET, + TipName[selected + i - arrow]); + } + } while (u8g2.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]; + uint16_t lastTargetTemp = heater.getTargetTemp(); + for (uint8_t CalStep = 0; CalStep < 3; CalStep++) { + heater.setTargetTemp( CalTemp[CurrentTip][CalStep] ); + LOGI(T_CTRL, print, "Target Temp: "); + LOGI(T_CTRL, println, heater.getTargetTemp()); + setRotary(100, 500, 1, heater.getTargetTemp()); + beepIfWorky = true; + bool lastbutton = (!digitalRead(BUTTON_PIN)); + + do { + thermostatCheck(); // heater control + + u8g2.firstPage(); + do { + u8g2.setFont(PTS200_16); + if(language == 2){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } + u8g2.setFontPosTop(); + // strcpy_P(F_Buffer, PSTR("校准")); + u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, txt_calibrate[language]); + u8g2.setCursor(0, 16 + SCREEN_OFFSET); + u8g2.print(txt_step[language]); + u8g2.print(CalStep + 1); + u8g2.print(" of 3"); + if (isWorky) { + u8g2.setCursor(0, 32 + SCREEN_OFFSET); + u8g2.print(txt_set_measured[language]); + u8g2.setCursor(0, 48 + SCREEN_OFFSET); + u8g2.print(txt_s_temp[language]); + u8g2.print(getRotary()); + } else { + u8g2.setCursor(0, 32 + SCREEN_OFFSET); + u8g2.print(txt_temp_2[language]); + u8g2.print(heater.getCurrentTemp()); + u8g2.setCursor(0, 48 + SCREEN_OFFSET); + u8g2.print(txt_wait_pls[language]); + } + } while (u8g2.nextPage()); + if (lastbutton && digitalRead(BUTTON_PIN)) { + delay(10); + lastbutton = false; + } + } while (digitalRead(BUTTON_PIN) || lastbutton); + + CalTempNew[CalStep] = getRotary(); + beep(); + delay(10); + } + + heater.disable(); // shut off heater 关闭加热器 + if (VoltageValue == 3) { + delayMicroseconds(TIME2SETTLE_20V); + } else { + delayMicroseconds(TIME2SETTLE); // wait for voltage to settle 等待电压稳定 + } + CalTempNew[3] = accel.getAccellTemp(); // 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]; + } + } + + heater.setTargetTemp( lastTargetTemp ); + update_EEPROM(); +} + +// 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); + } + u8g2.firstPage(); + do { + u8g2.setFont(PTS200_16); + if(language == 2){ + u8g2.setFont(u8g2_font_unifont_t_chinese3); + } + u8g2.setFontPosTop(); + u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, txt_enter_tip_name[language]); + u8g2.setCursor(12 * digit, 48 + SCREEN_OFFSET); + u8g2.print(char(94)); + u8g2.setCursor(0, 32 + SCREEN_OFFSET); + for (uint8_t i = 0; i < digit; i++) u8g2.print(TipName[CurrentTip][i]); + u8g2.setCursor(12 * digit, 32 + SCREEN_OFFSET); + u8g2.print(char(value)); + } while (u8g2.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)); +} + +unsigned int Button_Time1 = 0, Button_Time2 = 0; + +void Button_loop() { + // '-' decrementing button + if (!digitalRead(BUTTON_N_PIN) && a0 == 1) { + delay(BUTTON_DELAY); + if (!digitalRead(BUTTON_N_PIN)) { + int count0 = count; + count = constrain(count + countStep, countMin, countMax); + if (!(countMin == TEMP_MIN && countMax == TEMP_MAX)) { + if (count0 + countStep > countMax) { + count = countMin; + } + } + a0 = 0; + } + } else if (!digitalRead(BUTTON_N_PIN) && a0 == 0) { + delay(BUTTON_DELAY); + if (Button_Time1 > 10) // this value controls long-press timeout / 这里的数越大,需要长按时间更长 + count = constrain(count + countStep, countMin, countMax); + else + Button_Time1++; + } else if (digitalRead(BUTTON_N_PIN)) { + Button_Time1 = 0; + a0 = 1; + } + + // '+' incrementing button + if (!digitalRead(BUTTON_P_PIN) && b0 == 1) { + delay(BUTTON_DELAY); + if (!digitalRead(BUTTON_P_PIN)) { + int count0 = count; + count = constrain(count - countStep, countMin, countMax); + if (!(countMin == TEMP_MIN && countMax == TEMP_MAX)) { + if (count0 - countStep < countMin) { + count = countMax; + } + } + b0 = 0; + } + } else if (!digitalRead(BUTTON_P_PIN) && b0 == 0) { + delay(BUTTON_DELAY); + if (Button_Time2 > 10) // this value controls long-press timeout / 这里的数越大,需要长按时间更长 + count = constrain(count - countStep, countMin, countMax); + else + Button_Time2++; + } else if (digitalRead(BUTTON_P_PIN)) { + Button_Time2 = 0; + b0 = 1; + } +} + +// uint16_t calibrate_adc(adc_unit_t adc, adc_atten_t channel) { +// uint16_t vref; +// esp_adc_cal_characteristics_t adc_chars; +// esp_adc_cal_value_t val_type = esp_adc_cal_characterize((adc_unit_t)adc, +// (adc_atten_t)channel, (adc_bits_width_t)ADC_WIDTH_BIT_12, 1100, +// &adc_chars); +// //Check type of calibration value used to characterize ADC +// if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { +// Serial.printf("eFuse Vref:%u mV", adc_chars.vref); +// Serial.println(); +// vref = adc_chars.vref; +// } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { +// Serial.printf("Two Point --> coeff_a:%umV coeff_b:%umV\n", +// adc_chars.coeff_a, adc_chars.coeff_b); Serial.println(); +// } else { +// Serial.println("Default Vref: 1100mV"); +// } +// return vref; +// } + + +#endif \ No newline at end of file diff --git a/SolderingPen_ESP32S2/sensors.cpp b/SolderingPen_ESP32S2/sensors.cpp index 58ad929..ac79fbe 100644 --- a/SolderingPen_ESP32S2/sensors.cpp +++ b/SolderingPen_ESP32S2/sensors.cpp @@ -1,4 +1,5 @@ #include "sensors.hpp" +#include "nvs_handle.hpp" #include "const.h" #include "log.h" @@ -7,15 +8,22 @@ #define ACCEL_MOTION_POLL_PERIOD 50 // ms #define ACCEL_TEMPERATURE_POLL_PERIOD 1000 // ms +#define VIN_ADC_POLL_PERIOD 1000 // ms GyroSensor::~GyroSensor(){ + // unsubscribe from event bus + if (_evt_set_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_SET_EVT, e2int(evt::iron_t::sensorsReload), _evt_set_handler); + _evt_set_handler = nullptr; + } + xTimerDelete( _tmr_temp, portMAX_DELAY ); xTimerDelete( _tmr_accel, portMAX_DELAY ); _tmr_temp = nullptr; _tmr_accel = nullptr; }; -void GyroSensor::begin(){ +void GyroSensor::init(){ if (!accel.begin()) { LOGE(T_GYRO, println, "Accelerometer not detected."); } @@ -44,10 +52,22 @@ void GyroSensor::begin(){ static_cast(this), [](TimerHandle_t h) { static_cast(pvTimerGetTimerID(h))->_accel_poll(); } ); - // keep it disabled on begin - //xTimerStart( _tmr_accel, pdMS_TO_TICKS(10) ); + // keep it disabled on begin, TODO: realct on events from mode changes + xTimerStart( _tmr_accel, pdMS_TO_TICKS(10) ); } + // subscribe to event bus + if (!_evt_set_handler){ + esp_event_handler_instance_register_with( + evt::get_hndlr(), + IRON_SET_EVT, + e2int(evt::iron_t::sensorsReload), // subscribe to 'sensorsReload command' + [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->enable(); }, // trigger config and timers reload + this, + &_evt_set_handler + ); + } + // lis2dh12_block_data_update_set(&(accel.dev_ctx), PROPERTY_DISABLE); // accel.setScale(LIS2DH12_2g); // accel.setMode(LIS2DH12_HR_12bit); @@ -140,13 +160,13 @@ void GyroSensor::_accel_poll(){ // Serial.print(" "); // Serial.println(var[2]); - int varThreshold = _motionThreshold * ACCEL_MOTION_FACTOR; + uint32_t varThreshold = _motionThreshold * ACCEL_MOTION_FACTOR; if (var[0] > varThreshold || var[1] > varThreshold || var[2] > varThreshold) { _motion = true; _clear(); LOGD(T_GYRO, println, "motion detected!"); - LOGD(T_GYRO, printf, "Th:%d, x:%u, y:%u, z:%u\n", varThreshold, var[0], var[1], var[2]); + LOGV(T_GYRO, printf, "Th:%d, x:%u, y:%u, z:%u\n", varThreshold, var[0], var[1], var[2]); // post event with motion detect EVT_POST(SENSOR_DATA, e2int(evt::iron_t::motion)); } @@ -164,8 +184,20 @@ float GyroSensor::getAccellTemp() { void GyroSensor::enable(){ _clear(); + + esp_err_t err; + std::unique_ptr nvs = nvs::open_nvs_handle(T_Sensor, NVS_READONLY, &err); + + // load configured motion threshold + if (err != ESP_OK) { + nvs->get_item(T_motionThr, _motionThreshold); + } + + // start sensor polling if (_tmr_accel) xTimerStart( _tmr_accel, pdMS_TO_TICKS(10) ); + + LOGD(T_GYRO, printf, "gyro enabled, thr:%u\n", _motionThreshold); } void GyroSensor::disable(){ @@ -185,4 +217,84 @@ void GyroSensor::_clear(){ void GyroSensor::_temperature_poll(){ float t = getAccellTemp(); EVT_POST_DATA(SENSOR_DATA, e2int(evt::iron_t::acceltemp), &t, sizeof(t)); +} + + + +// *** VinSensor methods *** + +void VinSensor::init(){ + // input voltage pin ADC + adc_vin.attach(VIN_PIN); + + // start voltage polling + if (!_tmr_runner){ + _tmr_runner = xTimerCreate("vinADC", + pdMS_TO_TICKS(VIN_ADC_POLL_PERIOD), + pdTRUE, + static_cast(this), + [](TimerHandle_t h) { static_cast(pvTimerGetTimerID(h))->_runner(); } + ); + // start poller right away + xTimerStart( _tmr_runner, pdMS_TO_TICKS(10) ); + } + +/* + // subscribe to event bus + if (!_evt_set_handler){ + esp_event_handler_instance_register_with( + evt::get_hndlr(), + IRON_SET_EVT, + e2int(evt::iron_t::sensorsReload), // subscribe to 'sensorsReload command' + [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->enable(); }, // trigger config and timers reload + this, + &_evt_set_handler + ); + } +*/ +} + +VinSensor::~VinSensor(){ + xTimerDelete( _tmr_runner, portMAX_DELAY ); + _tmr_runner = nullptr; +} + +// get supply voltage in mV 得到以mV为单位的电源电压 +void VinSensor::_runner(){ + uint32_t voltage = 0; + + for (uint8_t i = 0; i < 4; i++) { // get 32 readings 得到32个读数 + voltage += adc_vin.readMiliVolts(); + } + voltage = voltage / 4 * 31.3f; + + // log and publish Vin value + LOGD(T_ADC, printf, "Vin: %u mV\n", voltage); + EVT_POST_DATA(SENSOR_DATA, e2int(evt::iron_t::vin), &voltage, sizeof(voltage)); + + + // some calibration calc + // // VIN_Ru = 100k, Rd_GND = 3.3K + // if (value < 500) + // { + // voltage = value * 1390 * 31.3 / 4095 * 1.35; + // } + // else if (500 <= value && value < 1000) + // { + // voltage = value * 1390 * 31.3 / 4095 * 1.135; + // } + // else if (1000 <= value && value < 1500) + // { + // voltage = value * 1390 * 31.3 / 4095 * 1.071; + // } + // else if (1500 <= value && value < 2000) + // { + // voltage = value * 1390 * 31.3 / 4095; + // } + // else if (2000 <= value && value < 3000) + // { + // voltage = value * 1390 * 31.3 / 4095; + // } + // else + // voltage = value * 1390 * 31.3 / 4095; } \ No newline at end of file diff --git a/SolderingPen_ESP32S2/sensors.hpp b/SolderingPen_ESP32S2/sensors.hpp index 7f14364..8ffe5cd 100644 --- a/SolderingPen_ESP32S2/sensors.hpp +++ b/SolderingPen_ESP32S2/sensors.hpp @@ -1,9 +1,8 @@ #pragma once #include #include "config.h" -// https://github.com/sparkfun/SparkFun_LIS2DH12_Arduino_Library -#include "SparkFun_LIS2DH12.h" -#include "ts.h" +#include "SparkFun_LIS2DH12.h" // https://github.com/sparkfun/SparkFun_LIS2DH12_Arduino_Library +#include "ESP32AnalogRead.h" // https://github.com/madhephaestus/ESP32AnalogRead #include "evtloop.hpp" #include "freertos/timers.h" @@ -16,7 +15,7 @@ class GyroSensor { }; bool _motion{false}; - int _motionThreshold{WAKEUP_THRESHOLD}; + uint32_t _motionThreshold{WAKEUP_THRESHOLD}; std::array samples; size_t accelIndex{0}; //uint16_t accels[32][3]; @@ -27,6 +26,8 @@ class GyroSensor { // accell sensor polling timer TimerHandle_t _tmr_accel = nullptr; + esp_event_handler_instance_t _evt_set_handler = nullptr; + /** * @brief clear sampling array * @@ -45,6 +46,9 @@ class GyroSensor { */ void _accel_poll(); + // events handler + void _eventHandler(esp_event_base_t base, int32_t id, void* data); + public: // d-tor @@ -54,7 +58,7 @@ class GyroSensor { * @brief initialize sensor * */ - void begin(); + void init(); /** * @brief enable motion sensor detector @@ -95,3 +99,35 @@ class GyroSensor { float getAccellTemp(); }; + +/** + * @brief Input voltage sensor + * will measure Vin periodically and report via event bus + * + */ +class VinSensor { + // RTOS polling timer + TimerHandle_t _tmr_runner = nullptr; + + //esp_event_handler_instance_t _evt_set_handler = nullptr; + + // ADC Calibrated Reader + ESP32AnalogRead adc_vin; + + // events handler + void _eventHandler(esp_event_base_t base, int32_t id, void* data); + + void _runner(); + +public: + // d-tor + ~VinSensor(); + + /** + * @brief initialize sensor + * + */ + void init(); + + +}; From 1285ba46061c9c355f32e006d7bb3e5822f32536 Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Mon, 22 Apr 2024 00:43:47 +0900 Subject: [PATCH 2/6] WIP - reimplementing screen menu from cratch Developing MuiPP lib to work on screen menu, navigation and control --- SolderingPen_ESP32S2/SolderingPen_ESP32S2.ino | 5 - SolderingPen_ESP32S2/evtloop.cpp | 4 +- SolderingPen_ESP32S2/evtloop.hpp | 2 +- SolderingPen_ESP32S2/heater.cpp | 4 +- SolderingPen_ESP32S2/heater.hpp | 2 +- SolderingPen_ESP32S2/hid.cpp | 243 ++++++++++++++---- SolderingPen_ESP32S2/hid.hpp | 151 +++++++---- SolderingPen_ESP32S2/ironcontroller.cpp | 9 +- .../lang/{dictionaries.h => dictionaries.h_} | 4 + SolderingPen_ESP32S2/lang/i18n.h | 10 +- SolderingPen_ESP32S2/lang/lang_en_us.h | 62 ++++- SolderingPen_ESP32S2/lang/lang_ru_ru.h | 43 +++- SolderingPen_ESP32S2/main.cpp | 114 ++------ SolderingPen_ESP32S2/sensors.cpp | 5 +- SolderingPen_ESP32S2/ts.cpp | 7 - SolderingPen_ESP32S2/ts.h | 9 - platformio.ini | 3 +- 17 files changed, 429 insertions(+), 248 deletions(-) delete mode 100644 SolderingPen_ESP32S2/SolderingPen_ESP32S2.ino rename SolderingPen_ESP32S2/lang/{dictionaries.h => dictionaries.h_} (81%) delete mode 100644 SolderingPen_ESP32S2/ts.cpp delete mode 100644 SolderingPen_ESP32S2/ts.h diff --git a/SolderingPen_ESP32S2/SolderingPen_ESP32S2.ino b/SolderingPen_ESP32S2/SolderingPen_ESP32S2.ino deleted file mode 100644 index b08122c..0000000 --- a/SolderingPen_ESP32S2/SolderingPen_ESP32S2.ino +++ /dev/null @@ -1,5 +0,0 @@ -/* -This file is just a stub to make Arduino IDE happy - -Pls, see main.cpp for main projet's code -*/ diff --git a/SolderingPen_ESP32S2/evtloop.cpp b/SolderingPen_ESP32S2/evtloop.cpp index b7816a2..bbeaf99 100644 --- a/SolderingPen_ESP32S2/evtloop.cpp +++ b/SolderingPen_ESP32S2/evtloop.cpp @@ -35,9 +35,9 @@ namespace evt { #define LOOP_EVT_PRIORITY 1 // task priority is same as arduino's loop() to avoid extra context switches #define LOOP_EVT_RUNNING_CORE tskNO_AFFINITY // ARDUINO_RUNNING_CORE #ifdef PTS200_DEBUG_LEVEL - #define LOOP_EVT_STACK_SIZE 2048 // loop task stack size when debug is enabled, sprintf calls requires lots of mem + #define LOOP_EVT_STACK_SIZE 4096 // loop task stack size when debug is enabled, sprintf calls requires lots of mem #else - #define LOOP_EVT_STACK_SIZE 1536 // loop task stack size + #define LOOP_EVT_STACK_SIZE 4096 // loop task stack size #endif void start(){ diff --git a/SolderingPen_ESP32S2/evtloop.hpp b/SolderingPen_ESP32S2/evtloop.hpp index 79fd62d..2427d93 100644 --- a/SolderingPen_ESP32S2/evtloop.hpp +++ b/SolderingPen_ESP32S2/evtloop.hpp @@ -57,7 +57,7 @@ enum class iron_t:int32_t { stateStandby, // iron controller switched to 'Standby' mode stateIdle, stateSuspend, - stateBoost, + stateBoost, // iron controller switched to 'Boost' mode, parameter uint32_t - seconds left to disable boost mode stateSetup, stateNoTip, tipEject, // sent by heater when it looses the tip sense diff --git a/SolderingPen_ESP32S2/heater.cpp b/SolderingPen_ESP32S2/heater.cpp index b7f7e32..215df05 100644 --- a/SolderingPen_ESP32S2/heater.cpp +++ b/SolderingPen_ESP32S2/heater.cpp @@ -1,7 +1,7 @@ -#include "heater.hpp" #include -#include "evtloop.hpp" #include "const.h" +#include "evtloop.hpp" +#include "heater.hpp" #include "main.h" #include "log.h" diff --git a/SolderingPen_ESP32S2/heater.hpp b/SolderingPen_ESP32S2/heater.hpp index 216e133..2f51ecb 100644 --- a/SolderingPen_ESP32S2/heater.hpp +++ b/SolderingPen_ESP32S2/heater.hpp @@ -1,6 +1,6 @@ #pragma once #include "config.h" -#include "FreeRTOS.h" +#include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/ledc.h" #include diff --git a/SolderingPen_ESP32S2/hid.cpp b/SolderingPen_ESP32S2/hid.cpp index 815a91d..d45c992 100644 --- a/SolderingPen_ESP32S2/hid.cpp +++ b/SolderingPen_ESP32S2/hid.cpp @@ -13,7 +13,8 @@ #include "hid.hpp" #include "nvs.hpp" #include "const.h" -#include // https://github.com/olikraus/u8g2 +//#include "U8g2lib.h" // https://github.com/olikraus/u8g2 +#include "lang/lang_en_us.h" #include "log.h" /* @@ -39,6 +40,8 @@ U8G2_SH1107_64X128_F_HW_I2C u8g2(U8G2_R1, SH1107_RST_PIN); #error Wrong OLED controller type! #endif +#define TIMER_DISPLAY_REFRESH 10 // 10 fps max (screen redraw is ~35 ms) + #define X_OFFSET_TIP_TEMP 50 #define Y_OFFSET_TIP_TEMP 18 @@ -54,13 +57,27 @@ using ESPButton::event_t; using evt::iron_t; -void IronScreen::init(){ +void IronHID::_init_screen(){ +#ifndef NO_DISPLAY u8g2.initDisplay(); u8g2.begin(); u8g2.sendF("ca", 0xa8, 0x3f); u8g2.enableUTF8Print(); +#endif - _viset = std::make_unique(); + // switch to default VisualSet + switchScreen(); + + if (!_tmr_display){ + _tmr_display = xTimerCreate("scrT", + pdMS_TO_TICKS(TIMER_DISPLAY_REFRESH), + pdTRUE, + static_cast(this), + [](TimerHandle_t h) { static_cast(pvTimerGetTimerID(h))->viset->drawScreen(); } + ); + if (_tmr_display) + xTimerStart( _tmr_display, portMAX_DELAY ); + } /* TBD if(_hand_side){ @@ -72,6 +89,21 @@ void IronScreen::init(){ } +void IronHID::switchScreen(vset_t v){ + // todo: do I need a locking mutex here? + LOGI(T_HID, printf, "switch screen to:%d\n", e2int(v)); + switch(v){ + case vset_t::mainScreen : + viset = std::make_unique(); + break; + case vset_t::configMenu : + viset = std::make_unique(); + break; + + }; +} + + // ********************* // *** IronHID *** @@ -81,7 +113,7 @@ IronHID::IronHID() : _encdr(BUTTON_DECR, BUTTON_INCR, LOW), _btn(BUTTON_ACTION, void IronHID::init(const Temperatures& t){ // initialize screen - _screen.init(); + _init_screen(); // link our event loop with ESPButton ESPButton::set_event_loop_hndlr(evt::get_hndlr()); @@ -114,8 +146,8 @@ void IronHID::init(const Temperatures& t){ ); } - // get initial working temp. - _wrk_temp = t.working; + // get initial temperatures + _temp = t; // enable middle button _btn.enable(); @@ -128,21 +160,33 @@ void IronHID::init(const Temperatures& t){ void IronHID::_set_button_menu_callbacks(){ // actions for middle button in main mode _menu.assign(BUTTON_ACTION, 0, [this](event_t e, const EventMsg* m){ _menu_0_main_mode(e, m); }); + _menu.assign(BUTTON_ACTION, 1, [this](event_t e, const EventMsg* m){ _menu_1_config_navigation(e, m); }); } // encoder events executor, it works in cooperation with _menu object void IronHID::_encoder_events(esp_event_base_t base, int32_t id, void* event_data){ + LOGD(T_HID, printf, "_encoder_events:%d, cnt:%d\n", id, reinterpret_cast(event_data)->cntr); switch(_menu.getMenuLevel()){ - // main working mode - encoder controls Iron temperature + + // main Iron screen mode - encoder controls Iron working temperature case 0 : { - _wrk_temp = reinterpret_cast(event_data)->cntr; - EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::workTemp), &_wrk_temp, sizeof(_wrk_temp)); + _temp.working = reinterpret_cast(event_data)->cntr; + EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::workTemp), &_temp.working, sizeof(_temp.working)); break; } - } - + // configuration Menu - encoder sends commands to MuiPP sink + case 1 : { + // we do not need counter value here (for now), just figure out if it was increment or decrement via gpio which triggered and event + if (reinterpret_cast(event_data)->gpio == BUTTON_INCR){ + viset->muipp_event( {mui_event_t::moveDown, 0, nullptr} ); + } else { + viset->muipp_event( {mui_event_t::moveUp, 0, nullptr} ); + } + break; + } + } } void IronHID::_switch_buttons_modes(uint32_t level){ @@ -155,14 +199,24 @@ void IronHID::_switch_buttons_modes(uint32_t level){ _btn.enableEvent(event_t::multiClick); // set encoder to control working temperature _encdr.reset(); - _encdr.setCounter(_wrk_temp, 5, TEMP_MIN, TEMP_MAX); + _encdr.setCounter(_temp.working, 5, TEMP_MIN, TEMP_MAX); _encdr.setMultiplyFactor(2); break; + // config menu navigation + case 1 : + _btn.deactivateAll(); + _btn.enableEvent(event_t::click); + // set encoder to control working temperature + _encdr.reset(); + //_encdr.setCounter(_temp.working, 5, TEMP_MIN, TEMP_MAX); + //_encdr.setMultiplyFactor(2); + break; + } } -// actions for middle button in main mode +// actions for middle button when iron is main working mode void IronHID::_menu_0_main_mode(ESPButton::event_t e, const EventMsg* m){ LOGD(T_HID, printf, "Button Menu lvl:0 e:%d\n", e); switch(e){ @@ -171,23 +225,42 @@ void IronHID::_menu_0_main_mode(ESPButton::event_t e, const EventMsg* m){ EVT_POST(IRON_SET_EVT, e2int(iron_t::workModeToggle)); break; - // use longPress to toggle boost mode + // use longPress to enter configuration menu case event_t::longPress : - EVT_POST(IRON_SET_EVT, e2int(iron_t::boostModeToggle)); + _menu.setMenuLevel(1); // switch button menu to mainConfig mode + _switch_buttons_modes(1); // switch button events to navigate in menu + switchScreen(vset_t::configMenu); break; // pick multiClicks case event_t::multiClick : - // todo: doubleclick for entering Menu - //EVT_POST(IRON_SET_EVT, e2int(iron_t::boostModeToggle)); + // doubleclick to toggle boost mode + if (m->cntr == 2) + EVT_POST(IRON_SET_EVT, e2int(iron_t::boostModeToggle)); break; } +} + +// actions for middle button in main Configuration menu +void IronHID::_menu_1_config_navigation(ESPButton::event_t e, const EventMsg* m){ + LOGD(T_HID, printf, "Button Menu lvl:1 e:%d\n", e); + switch(e){ + // Use click event to send Select to MUI menu + case event_t::click : + viset->muipp_event( mui_event(mui_event_t::enter) ); + break; + // use longPress to escape current item + case event_t::longPress : + viset->muipp_event( mui_event(mui_event_t::escape) ); + break; + } } -// ***** VisualSet ***** + +// ***** VisualSet - Main Screen ***** VisualSet::VisualSet() { // subscribe to all events on a bus ESP_ERROR_CHECK(esp_event_handler_instance_register_with( @@ -196,14 +269,6 @@ VisualSet::VisualSet() { VisualSet::_event_picker, this, &_evt_handler) ); - - // loag UI language index - esp_err_t err; - std::unique_ptr nvs = nvs::open_nvs_handle(T_UI, NVS_READONLY, &err); - - if (err == ESP_OK) { - nvs->get_item(T_lang, lang); - } } VisualSet::~VisualSet(){ @@ -229,7 +294,7 @@ void VisualSet::_event_picker(void* arg, esp_event_base_t base, int32_t id, void return reinterpret_cast(arg)->_evt_state(id, event_data); } -VisualSetMainScreen::VisualSetMainScreen() : VisualSet(){ +ViSet_MainScreen::ViSet_MainScreen() : VisualSet(){ // request working temperature from IronController EVT_POST(IRON_GET_EVT, e2int(iron_t::workTemp)); @@ -237,13 +302,11 @@ VisualSetMainScreen::VisualSetMainScreen() : VisualSet(){ //_drawMainScreen(); } -void VisualSetMainScreen::_drawMainScreen(){ +void ViSet_MainScreen::drawScreen(){ u8g2.clearBuffer(); - if(lang == 0){ - u8g2.setFont(u8g2_font_unifont_t_chinese3); - } else - u8g2.setFont(PTS200_16); + u8g2.setFont(u8g2_font_unifont_t_chinese3); + //u8g2.setFont(PTS200_16); u8g2.setFontPosTop(); @@ -251,19 +314,19 @@ void VisualSetMainScreen::_drawMainScreen(){ u8g2.setCursor(0, 0 + SCREEN_OFFSET); switch (_state){ case ironState_t::idle : - u8g2.print(dict[lang][D_idle]); + u8g2.print(dictionary[D_idle]); break; case ironState_t::working : - u8g2.print(dict[lang][D_heating]); + u8g2.print(dictionary[D_heating]); break; case ironState_t::standby : - u8g2.print(dict[lang][D_standby]); + u8g2.print(dictionary[D_standby]); break; case ironState_t::boost : - u8g2.print(dict[lang][D_boost]); + u8g2.print(dictionary[D_boost]); break; case ironState_t::notip : - u8g2.print(dict[lang][D_notip]); + u8g2.print(dictionary[D_notip]); break; default:; @@ -271,13 +334,24 @@ void VisualSetMainScreen::_drawMainScreen(){ // print working temperature in the upper right corner // u8g2.drawUTF8(0, 0 + SCREEN_OFFSET, "设温:"); - //u8g2.drawUTF8(OFFSET_TARGET_TEMP_1, 0 + SCREEN_OFFSET, dict[lang][D_set_t]); // target temperature + //u8g2.drawUTF8(OFFSET_TARGET_TEMP_1, 0 + SCREEN_OFFSET, dictionary[D_set_t]); // target temperature //u8g2.setFont(u8g2_font_unifont_t_chinese3); u8g2.setCursor(OFFSET_TARGET_TEMP_1, 0 + SCREEN_OFFSET); //u8g2.drawUTF8(OFFSET_TARGET_TEMP_1, 0 + SCREEN_OFFSET, "T:"); // target temperature //u8g2.setCursor(OFFSET_TARGET_TEMP_2, 0 + SCREEN_OFFSET); u8g2.print("T:"); - u8g2.print(_wrk_temp, 0); + // print target temperature depending on Iron work state + switch(_state){ + case ironState_t::standby : + u8g2.print(_temp.standby, 0); + break; + case ironState_t::boost : + u8g2.print(_temp.working + _temp.boost, 0); + break; + default: + u8g2.print(_temp.working, 0); + break; + } u8g2.print("C"); /* @@ -286,7 +360,7 @@ void VisualSetMainScreen::_drawMainScreen(){ } if (_tip_temp > TEMP_NOTIP) - u8g2.print(dict[lang][D_error]); + u8g2.print(dictionary[D_error]); else if (inOffMode) u8g2.print(txt_off[language]); else if (inSleepMode) @@ -336,7 +410,7 @@ void VisualSetMainScreen::_drawMainScreen(){ u8g2.sendBuffer(); } -void VisualSetMainScreen::_evt_sensor(int32_t id, void* data){ +void ViSet_MainScreen::_evt_sensor(int32_t id, void* data){ switch(static_cast(id)){ case evt::iron_t::tiptemp : _tip_temp = *reinterpret_cast(data); @@ -352,10 +426,10 @@ void VisualSetMainScreen::_evt_sensor(int32_t id, void* data){ return; } - _drawMainScreen(); + //_drawMainScreen(); } -void VisualSetMainScreen::_evt_notify(int32_t id, void* data){ +void ViSet_MainScreen::_evt_notify(int32_t id, void* data){ switch(static_cast(id)){ case evt::iron_t::stateIdle : _state = ironState_t::idle; @@ -372,23 +446,96 @@ void VisualSetMainScreen::_evt_notify(int32_t id, void* data){ } } -void VisualSetMainScreen::_evt_cmd(int32_t id, void* data){ +void ViSet_MainScreen::_evt_cmd(int32_t id, void* data){ switch(static_cast(id)){ case evt::iron_t::workTemp : - _wrk_temp = *reinterpret_cast(data); + _temp.working = *reinterpret_cast(data); break; } } -void VisualSetMainScreen::_evt_state(int32_t id, void* data){ +void ViSet_MainScreen::_evt_state(int32_t id, void* data){ switch(static_cast(id)){ case evt::iron_t::workTemp : - _wrk_temp = *reinterpret_cast(data); + _temp.working = *reinterpret_cast(data); break; } } + +// ***** VisualSet - ConfigurationMenu ***** + +ViSet_ConfigurationMenu::ViSet_ConfigurationMenu(){ + _build_menu(); +}; + +void ViSet_ConfigurationMenu::_build_menu(){ + LOGD(T_HID, println, "Build menu"); + + // create page with Settings options list + muiItem_id cfg_page = makePage(dictionary[D_Settings]); + +#define MAKE_ITEM(ITEM_TYPE, ARG, PAGE) { std::unique_ptr p = std::make_unique< ITEM_TYPE > ARG; addMuippItem(std::move(p), PAGE); } + + MAKE_ITEM( MuiItem_U8g2_PageTitle, (u8g2, nextIndex(), MAIN_MENU_FONT1 ), cfg_page); // u8g2_font_unifont_t_chinese3 + + //std::unique_ptr p = std::make_unique(u8g2, muipp.nextIndex(), u8g2_font_unifont_t_chinese3); + //muipp.addMuippItem(std::move(p), cfg_page); + + //MAKE_ITEM( MuiItem_U8g2_PageTitle, (u8g2, muipp.nextIndex(), MAIN_MENU_FONT2, 10, 15), cfg_page); + //std::unique_ptr p = std::make_unique(u8g2, muipp.nextIndex(), nullptr, 15, 15); + //muipp.addMuippItem(std::move(p), cfg_page); + + + //MAKE_ITEM( MuiItem_U8g2_PageTitle, (u8g2, muipp.nextIndex(), MAIN_MENU_FONT3, 10, 30), cfg_page); + //std::unique_ptr p = std::make_unique(u8g2, muipp.nextIndex(), nullptr, 30, 50); + //muipp.addMuippItem(std::move(p), cfg_page); + + // create and add to main page a list with settings selection options + muiItem_id menu_scroll_item = nextIndex(); + std::unique_ptr p = std::make_unique(u8g2, menu_scroll_item, + [](size_t index){ Serial.printf("idx:%u\n", index); return menu_MainConfiguration.at(index); }, // this lambda will feed localized strings to the MuiItem list builder class + menu_MainConfiguration.size(), + MAIN_MENU_Y_SHIFT, 3, // offset for each line of text and total number of lines in menu + MAIN_MENU_FONT2, MAIN_MENU_FONT3, MAIN_MENU_X_OFFSET, MAIN_MENU_Y_OFFSET + ); + addMuippItem(std::move(p), cfg_page); + + menuStart(cfg_page, menu_scroll_item); +} + + +void ViSet_ConfigurationMenu::drawScreen(){ + if (!refresh_req) return; + + Serial.printf("st:%lu\n", millis()); + u8g2.clearBuffer(); + render(); + u8g2.sendBuffer(); + Serial.printf("en:%lu\n", millis()); + + refresh_req = false; +} + +mui_event ViSet_ConfigurationMenu::muipp_event(mui_event e) { + //LOGD(T_HID, printf, "Got event for muipp lvl:1 e:%u\n", e.eid); + + // any event will trigger screen refresh + refresh_req = true; + + //return {}; + // forward those messages to muipp + return muiEvent(e); +} + + +// ***************************** +// *** MUI entities + + + + // ***************************** // *** An instance of HID object -IronHID hid; \ No newline at end of file +IronHID hid; diff --git a/SolderingPen_ESP32S2/hid.hpp b/SolderingPen_ESP32S2/hid.hpp index 20abcb7..23d9302 100644 --- a/SolderingPen_ESP32S2/hid.hpp +++ b/SolderingPen_ESP32S2/hid.hpp @@ -13,9 +13,7 @@ #include "common.hpp" #include "evtloop.hpp" #include "espasyncbutton.hpp" -#include "lang/dictionaries.h" -#include - +#include "muipp_u8g2.h" /** * @brief A generic screen object instance @@ -24,13 +22,15 @@ * Specific implementation depends on derived class */ class VisualSet { + esp_event_handler_instance_t _evt_handler = nullptr; // event dispatcher static void _event_picker(void* arg, esp_event_base_t base, int32_t id, void* event_data); protected: - lang_index lang{L_en_us}; + // flag that shows a screen must be refreshed + bool refresh_req{true}; // sensor events picker virtual void _evt_sensor(int32_t id, void* event_data){}; @@ -46,67 +46,31 @@ class VisualSet { VisualSet(); virtual ~VisualSet(); -}; - - -/** - * @brief Main Iron screen display - * show working mode and basic sensors information - * - */ -class VisualSetMainScreen : public VisualSet { - ironState_t _state{0}; - int32_t _wrk_temp{0}, _tip_temp{0}; - // sensor temp - float _sns_temp{0}; - // input voltage - uint32_t _vin{0}; - - // renders Main working screen - void _drawMainScreen(); - - // sensor events handler - void _evt_sensor(int32_t id, void* data) override; - - // notify events handler - void _evt_notify(int32_t id, void* data) override; - - // command events handler - void _evt_cmd(int32_t id, void* data) override; - - // state events handler - void _evt_state(int32_t id, void* data) override; - -public: - VisualSetMainScreen(); + // MuiPlusPlus event sink, might be used in derrived classes + virtual mui_event muipp_event(mui_event e){ return {}; }; + // draw on screen information + virtual void drawScreen() = 0; }; -/** - * @brief and object that controls the screen and generates visual info - * - */ -class IronScreen { - - bool _hand_side; - - // an instance of visual oject that represents displayed info on a screen - std::unique_ptr _viset; - -public: - - void init(); - -}; /** * @brief an object that manages button controls and Screen navigation * */ class IronHID { - - // Display object - IronScreen _screen; + // a set of availbale "screens" + enum class vset_t { + mainScreen = 0, + configMenu + }; + + // Display object - an instance of visual set that represents displayed info on a screen + std::unique_ptr viset; + // screen redraw timer + TimerHandle_t _tmr_display = nullptr; + // flag that shows a screen must be refreshed + //bool refresh_req{true}; // Two button pseudo-encoder PseudoRotaryEncoder _encdr; @@ -124,7 +88,8 @@ class IronHID { esp_event_handler_instance_t _enc_evt_handler = nullptr; // target temperature - int32_t _wrk_temp; + Temperatures _temp; + // encoder events executor, it works in cooperation with _menu object void _encoder_events(esp_event_base_t base, int32_t id, void* event_data); @@ -135,12 +100,21 @@ class IronHID { // switch between different menu modes for action button void _switch_buttons_modes(uint32_t level); + /** + * @brief initialize screen + * will set default VisualSet as a screen renderer + * + */ + void _init_screen(); + /** * @brief button actions when iron is main working mode * */ void _menu_0_main_mode(ESPButton::event_t e, const EventMsg* m); + void _menu_1_config_navigation(ESPButton::event_t e, const EventMsg* m); + public: // c-tor IronHID(); @@ -153,8 +127,71 @@ class IronHID { */ void init(const Temperatures& t); + /** + * @brief switch to another instance of VisualSet's object + * this method will spawn a new instance of screen renderer depending on requested paramenter + * + */ + void switchScreen(vset_t v = vset_t::mainScreen); + }; + +/** + * @brief Main Iron screen display + * show working mode and basic sensors information + * + */ +class ViSet_MainScreen : public VisualSet { + ironState_t _state{0}; + Temperatures _temp{0}; + int32_t _tip_temp{0}; + // sensor temp + float _sns_temp{0}; + // input voltage + uint32_t _vin{0}; + + // renders Main working screen + void drawScreen() override; + + // sensor events handler + void _evt_sensor(int32_t id, void* data) override; + + // notify events handler + void _evt_notify(int32_t id, void* data) override; + + // command events handler + void _evt_cmd(int32_t id, void* data) override; + + // state events handler + void _evt_state(int32_t id, void* data) override; + +public: + ViSet_MainScreen(); + +}; + +/** + * @brief this class will draw and navigate through configuration menu + * + */ +class ViSet_ConfigurationMenu : public VisualSet, public MuiPlusPlus { + + void _build_menu(); + +public: + ViSet_ConfigurationMenu(); + + // MuiPlusPlus event sink, might be used in derrived classes + mui_event muipp_event(mui_event e) override; + + void drawScreen() override; + +}; + + + +// ************************** // Our global instance of HID extern IronHID hid; diff --git a/SolderingPen_ESP32S2/ironcontroller.cpp b/SolderingPen_ESP32S2/ironcontroller.cpp index 5404ce2..dc232e8 100644 --- a/SolderingPen_ESP32S2/ironcontroller.cpp +++ b/SolderingPen_ESP32S2/ironcontroller.cpp @@ -114,11 +114,16 @@ void IronController::_mode_switcher(){ } case ironState_t::boost : { - if (pdTICKS_TO_MS(xTaskGetTickCount()) - pdTICKS_TO_MS(_xTicks.boost) > _timeout.boost){ + unsigned t = pdTICKS_TO_MS(xTaskGetTickCount()) - pdTICKS_TO_MS(_xTicks.boost); + if (t > _timeout.boost){ _state = ironState_t::working; // notify other componets that we are switching to 'working' mode EVT_POST(IRON_NOTIFY, e2int(iron_t::stateWorking)); LOGI(T_CTRL, printf, "Engage work mode due to boost timeout of %u ms\n", _timeout.boost); + } else { + // send notification with time left till boost is disabled (in seconds) + unsigned time_left = _timeout.boost - t; + EVT_POST_DATA(IRON_NOTIFY, e2int(iron_t::stateBoost), &time_left, sizeof(time_left)); } } @@ -188,7 +193,7 @@ void IronController::_evt_commands(esp_event_base_t base, int32_t id, void* data _state = ironState_t::boost; // notify other components LOGI(T_CTRL, println, "switch to Boost mode"); - EVT_POST(IRON_NOTIFY, e2int(iron_t::stateBoost)); + EVT_POST_DATA(IRON_NOTIFY, e2int(iron_t::stateBoost), &_timeout.boost, sizeof(_timeout.boost)); // set heater to boost temperature int32_t t = _temp.working + _temp.boost; EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::heaterTargetT), &t, sizeof(t)); diff --git a/SolderingPen_ESP32S2/lang/dictionaries.h b/SolderingPen_ESP32S2/lang/dictionaries.h_ similarity index 81% rename from SolderingPen_ESP32S2/lang/dictionaries.h rename to SolderingPen_ESP32S2/lang/dictionaries.h_ index ebd1e7a..e51d67b 100644 --- a/SolderingPen_ESP32S2/lang/dictionaries.h +++ b/SolderingPen_ESP32S2/lang/dictionaries.h_ @@ -24,3 +24,7 @@ static constexpr std::array< std::array, L____SIZE> dic lang_ru_ru::dictionary }; +static constexpr std::array< std::array, L____SIZE> menu_Main = { + lang_en_us::menu_MainConfiguration, + lang_ru_ru::menu_MainConfiguration +}; diff --git a/SolderingPen_ESP32S2/lang/i18n.h b/SolderingPen_ESP32S2/lang/i18n.h index a2a5248..93fe2e6 100644 --- a/SolderingPen_ESP32S2/lang/i18n.h +++ b/SolderingPen_ESP32S2/lang/i18n.h @@ -10,15 +10,22 @@ (at your option) any later version. */ #pragma once +#include "MUIU8g2.h" + +#define MENU_MAIN_CFG_SIZE 8 +//#define GP_DICT_SIZE 7 + +/* // List of available translations -enum lang_index { +enum lang_index : uint32_t { L_en_us = (0), L_ru_ru, // L_zh_cn, // L_zh_tw, L____SIZE }; +*/ /** * Text-Dictionary Enums for language resources @@ -30,6 +37,7 @@ enum dict_index { D_heating, D_idle, D_notip, + D_Settings, D_set_t, D_standby, D_____SIZE diff --git a/SolderingPen_ESP32S2/lang/lang_en_us.h b/SolderingPen_ESP32S2/lang/lang_en_us.h index 2bf65da..081c22f 100644 --- a/SolderingPen_ESP32S2/lang/lang_en_us.h +++ b/SolderingPen_ESP32S2/lang/lang_en_us.h @@ -13,6 +13,20 @@ #include #include "i18n.h" +// 12x16 wrong? small gothic font +#define MAIN_MENU_FONT1 u8g2_font_glasstown_nbp_t_all +// 10x14 OK! +#define MAIN_MENU_FONT2 u8g2_font_smart_patrol_nbp_tr +// 12x17 +#define MAIN_MENU_FONT3 u8g2_font_shylock_nbp_t_all +// 7x7 +//#define MAIN_MENU_FONT3 u8g2_font_mercutio_sc_nbp_t_all + +#define MAIN_MENU_X_OFFSET 10 +#define MAIN_MENU_Y_OFFSET 12 +#define MAIN_MENU_Y_SHIFT 15 + + // EN-US namespace lang_en_us { @@ -21,19 +35,43 @@ static constexpr const char* T_Boost = "Boost"; static constexpr const char* T_Error = "Error"; static constexpr const char* T_Heating = "Heating"; static constexpr const char* T_Idle = "Idle"; -static constexpr const char* T_NoTip = "No tip!"; // state display when tip is missing -static constexpr const char* T_setT = "Set:"; // Target temperature on main screen -static constexpr const char* T_standby = "Standby"; // state display in 'Standby' +static constexpr const char* T_NoTip = "No tip!"; // state display when tip is missing +static constexpr const char* T_setT = "Set:"; // Target temperature on main screen +static constexpr const char* T_standby = "Standby"; // state display in 'Standby' +static constexpr const char* T_return = " dictionary = { - T_Boost, - T_Error, - T_Heating, - T_Idle, - T_NoTip, - T_setT, - T_standby -}; +// Menu Page names +static constexpr const char* T_Settings = "Settings"; + +// Main configuration menu +static constexpr const char* T_TipSettings = "Tip Настройки"; +static constexpr const char* T_TempSettings = "Temp Настройки"; +static constexpr const char* T_TimersSettings = "Timers Настройки"; +static constexpr const char* T_Information = "Информация"; +static constexpr const char* T_Language = "Language"; } // end of namespace lang_en_us +// general purpose dictionary +static constexpr std::array dictionary = { + lang_en_us::T_Boost, + lang_en_us::T_Error, + lang_en_us::T_Heating, + lang_en_us::T_Idle, + lang_en_us::T_NoTip, + lang_en_us::T_Settings, + lang_en_us::T_setT, + lang_en_us::T_standby +}; + +// Main Configuration menu items +static constexpr std::array menu_MainConfiguration = { + lang_en_us::T_TempSettings, + lang_en_us::T_TimersSettings, + lang_en_us::T_TipSettings, + lang_en_us::T_Information, + lang_en_us::T_Language, + lang_en_us::T_standby, + lang_en_us::T_Heating, + lang_en_us::T_return +}; diff --git a/SolderingPen_ESP32S2/lang/lang_ru_ru.h b/SolderingPen_ESP32S2/lang/lang_ru_ru.h index 992b06e..b3d819f 100644 --- a/SolderingPen_ESP32S2/lang/lang_ru_ru.h +++ b/SolderingPen_ESP32S2/lang/lang_ru_ru.h @@ -13,6 +13,19 @@ #include #include "i18n.h" + +// 10x16 contains lat/cyr chars +#define MAIN_MENU_FONT3 u8g2_font_mercutio_sc_nbp_t_all + +// 12x16 +// u8g2_font_glasstown_nbp_t_all + +// 10x15 https://github.com/olikraus/u8g2/wiki/fntgrpnbp +//u8g2_font_nine_by_five_nbp_t_all + +// 11x17 https://github.com/olikraus/u8g2/wiki/fntgrpnbp +//u8g2_font_guildenstern_nbp_t_all + // RU-RU namespace lang_ru_ru { @@ -20,19 +33,39 @@ static constexpr const char* T_Boost = "Разгон"; static constexpr const char* T_Error = "Ошибка"; static constexpr const char* T_Heating = "Нагрев"; static constexpr const char* T_Idle = "Отключен"; -static constexpr const char* T_NoTip = "Нет жала!"; // state display when tip is missing -static constexpr const char* T_set_T = "Уст:"; -static constexpr const char* T_standby = "Ожидание"; // state display in 'Standby' +static constexpr const char* T_NoTip = "Нет жала!"; // state display when tip is missing +static constexpr const char* T_setT = "Уст:"; +static constexpr const char* T_standby = "Ожидание"; // state display in 'Standby' +static constexpr const char* T_return = "<Выход"; // return back in menu's -// RU-RU +// Main configuration menu +static constexpr const char* T_TipSettings = MUI_10 "Настройки жал"; +static constexpr const char* T_TempSettings = MUI_20 "Настройки температуры"; +static constexpr const char* T_TimersSettings = MUI_30 "Настройки таймеров"; +static constexpr const char* T_Information = MUI_40 "Информация"; + +// general purpose dictionary static constexpr std::array dictionary = { T_Boost, T_Error, T_Heating, T_Idle, T_NoTip, - T_set_T + T_setT, + T_standby +}; + +// Main Configuration menu items +static constexpr std::array menu_MainConfiguration = { + T_TempSettings, + T_TimersSettings, + T_TipSettings, + T_Information, + lang_en_us::T_Language, + T_return }; + + } // end of namespace lang_ru_ru diff --git a/SolderingPen_ESP32S2/main.cpp b/SolderingPen_ESP32S2/main.cpp index ef9317a..f5c6fa0 100644 --- a/SolderingPen_ESP32S2/main.cpp +++ b/SolderingPen_ESP32S2/main.cpp @@ -1,21 +1,24 @@ // -#include "main.h" +#include "const.h" #include "ironcontroller.hpp" #include "heater.hpp" #include "sensors.hpp" #include "hid.hpp" -#include "ts.h" -#include "const.h" +#include "main.h" #include "log.h" #include +#include "UtilsEEPROM.h" +#ifdef CONFIG_TINYUSB_MSC_ENABLED #include "FirmwareMSC.h" #include "USB.h" -#include "UtilsEEPROM.h" +#endif -QC3Control QC(QC_DP_PIN, QC_DM_PIN); +#ifdef CONFIG_TINYUSB_MSC_ENABLED +QC3Control QC(QC_DP_PIN, QC_DM_PIN); FirmwareMSC MSC_Update; +#endif // Iron tip heater object TipHeater heater(HEATER_PIN, HEATER_CHANNEL, HEATER_INVERT); @@ -77,11 +80,13 @@ void setup() { digitalWrite(PD_CFG_0, HIGH); Serial.begin(115200); +#ifdef ARDUINO_USB_MODE Serial.setTxTimeoutMs(0); +#endif #if PTS200_DEBUG_LEVEL > 3 // let ACM device intitialize to catch early log output - delay(3000); + delay(4000); #endif LOGI(T_IRON, printf, "ESPIron PTS200 firmware version: %s\n", FW_VERSION); @@ -106,6 +111,8 @@ void setup() { getEEPROM(); // fake readings for now int VoltageValue = 20; + +#ifdef CONFIG_TINYUSB_MSC_ENABLED if (QCEnable) { QC.begin(); delay(100); @@ -129,124 +136,51 @@ void setup() { break; } } +#endif // Initialize IronController espIron.init(); // request configured voltage via PD trigger - PD_Update(); + //PD_Update(); // I2C bus (for display) Wire.begin(); Wire.setClock(100000); // 400000 // initialize heater - heater.init(); + //heater.init(); // initialize acceleration sensor - accel.init(); + //accel.init(); // intit voltage sensor - vin.init(); + //vin.init(); // initialize HID (buttons controls and navigation, screen) + LOGI(T_IRON, println, "Init HID"); hid.init(espIron.getTemperatures()); // long beep for setup completion 安装完成时长哔哔声 beep(); - beep(); + //beep(); } void loop() { - - ts.execute(); - - //ROTARYCheck(); // check rotary encoder (temp/boost setting, enter setup menu) - // 检查旋转编码器(温度/升压设置,进入设置菜单) - - //MainScreen(); // updates the main page on the OLED 刷新OLED主界面 -} -/* -// check rotary encoder; set temperature, toggle boost mode, enter setup menu -// accordingly 检查旋转编码器;设置温度,切换升压模式,进入设置菜单相应 -void ROTARYCheck() { - // set working temperature according to rotary encoder value - // 根据旋转编码器值设定工作温度 - if (!inSleepMode && !inOffMode) - heater.setTargetTemp( getRotary() ); - - uint8_t c = digitalRead(BUTTON_PIN); - if (!c && c0) { - delay(10); - if (digitalRead(BUTTON_PIN) == c) { - beep(); - buttonmillis = millis(); - delay(10); - while ((!digitalRead(BUTTON_PIN)) && ((millis() - buttonmillis) < 500)) - ; - delay(10); - if ((millis() - buttonmillis) >= 500) { - SetupScreen(); - } else { - if (inOffMode) { - inOffMode = false; - heater.enable(); // enable Tip Heater - accel.enable(); // enable Gyro sensor - } else { - buttonmillis = millis(); - while ((digitalRead(BUTTON_PIN)) && ((millis() - buttonmillis) < 200)) - delay(10); - if ((millis() - buttonmillis) >= 200) { // single click - if (inOffMode) { - // enable heater - inOffMode = false; - inSleepMode = false; - heater.enable(); - accel.enable(); - //u8g2.setPowerSave(0); - } else { - inBoostMode = !inBoostMode; - if (inBoostMode) { - boostmillis = millis(); - } - } - } else { // double click - // disable heater - inOffMode = true; - heater.disable(); - accel.disable(); - } - } - } - } - } - c0 = c; - - // check timer when in boost mode 在升温模式时检查计时器 - if (inBoostMode && espIron._timeout.boost) { - if ((millis() - boostmillis) / 1000 >= espIron._timeout.boost) { - inBoostMode = false; // stop boost mode 停止升温模式 - beep(); // beep if boost mode is over 如果升温模式结束,会发出蜂鸣声 - beepIfWorky = true; // beep again when working temperature is reached - // 当达到工作温度,会发出蜂鸣声 - } - } + // I do not need Arduino's loop, so this thread should be terminated + vTaskDelete(NULL); } -*/ - - - // creates a short beep on the buzzer 在蜂鸣器上创建一个短的哔哔声 void beep() { - if (beepEnable) { +// if (beepEnable) { for (uint8_t i = 0; i < 255; i++) { digitalWrite(BUZZER_PIN, HIGH); delayMicroseconds(125); digitalWrite(BUZZER_PIN, LOW); delayMicroseconds(125); } - } +// } } // reads user settings from EEPROM; if EEPROM values are invalid, write defaults diff --git a/SolderingPen_ESP32S2/sensors.cpp b/SolderingPen_ESP32S2/sensors.cpp index ac79fbe..4b12449 100644 --- a/SolderingPen_ESP32S2/sensors.cpp +++ b/SolderingPen_ESP32S2/sensors.cpp @@ -26,12 +26,9 @@ GyroSensor::~GyroSensor(){ void GyroSensor::init(){ if (!accel.begin()) { LOGE(T_GYRO, println, "Accelerometer not detected."); + return; } -// _tPoller.set(ACCEL_MOTION_POLL_PERIOD, TASK_FOREVER, [this](){ _poll(); }); -// ts.addTask(_tPoller); -// _tPoller.enable(); - // start temperature polling if (!_tmr_temp){ _tmr_temp = xTimerCreate("gyroT", diff --git a/SolderingPen_ESP32S2/ts.cpp b/SolderingPen_ESP32S2/ts.cpp deleted file mode 100644 index c9ff69a..0000000 --- a/SolderingPen_ESP32S2/ts.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#define _TASK_STD_FUNCTION // Compile with support for std::function -#define _TASK_SCHEDULING_OPTIONS -#define _TASK_SELF_DESTRUCT -#include - -// TaskScheduler - Let the runner object be a global, single instance shared between object files. -Scheduler ts; diff --git a/SolderingPen_ESP32S2/ts.h b/SolderingPen_ESP32S2/ts.h deleted file mode 100644 index bd712c7..0000000 --- a/SolderingPen_ESP32S2/ts.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -// Task Scheduler lib https://github.com/arkhipenko/TaskScheduler -#define _TASK_STD_FUNCTION // Compile with support for std::function. -#define _TASK_SCHEDULING_OPTIONS -#define _TASK_SELF_DESTRUCT -#include "TaskSchedulerDeclarations.h" - -// TaskScheduler - Let the runner object be a global, single instance shared between object files. -extern Scheduler ts; diff --git a/platformio.ini b/platformio.ini index 2395749..10a1e07 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,14 +13,13 @@ board = PTS200 [env:pts200] extends = pts200_base lib_deps = - ;lennarthennigs/Button2 @ ^2.2.2 + https://github.com/vortigont/ESPAsyncButton vdeconinck/QC3Control @ ^1.4.1 olikraus/U8g2 @ ^2.34.17 madhephaestus/ESP32AnalogRead @ ^0.2.1 ;br3ttb/PID @ ^1.2.1 mike-matera/FastPID @ ~1.3 sparkfun/SparkFun LIS2DH12 Arduino Library @ ^1.0.3 - arkhipenko/TaskScheduler @ ~3.7 build_src_flags = -std=gnu++17 build_unflags = From ed9a94f055f16eb68f660a1b630893e5f5f276a1 Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Sat, 11 May 2024 13:28:50 +0900 Subject: [PATCH 3/6] config menu list and other - make building with user-spicified config files - implement working (but empty) configuration menu --- .gitignore | 3 +- .../{UtilsEEPROM.cpp => UtilsEEPROM.cpp_} | 0 .../{UtilsEEPROM.h => UtilsEEPROM.h_} | 0 SolderingPen_ESP32S2/common.hpp | 5 + SolderingPen_ESP32S2/config.h | 17 ++-- SolderingPen_ESP32S2/heater.hpp | 2 +- SolderingPen_ESP32S2/hid.cpp | 91 ++++++++++++++++--- SolderingPen_ESP32S2/lang/i18n.h | 6 +- SolderingPen_ESP32S2/lang/lang_en_us.h | 29 ++++-- SolderingPen_ESP32S2/main.cpp | 11 --- SolderingPen_ESP32S2/sensors.hpp | 2 +- 11 files changed, 114 insertions(+), 52 deletions(-) rename SolderingPen_ESP32S2/{UtilsEEPROM.cpp => UtilsEEPROM.cpp_} (100%) rename SolderingPen_ESP32S2/{UtilsEEPROM.h => UtilsEEPROM.h_} (100%) diff --git a/.gitignore b/.gitignore index 770d7de..895fb10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -.pioenvs -.piolibdeps .clang_complete .gcc-flags.json .pio @@ -10,3 +8,4 @@ platformio_*.ini SolderingPen_ESP32S2/build SolderingPen_ESP32S2/.idea +SolderingPen_ESP32S2/config_*.h diff --git a/SolderingPen_ESP32S2/UtilsEEPROM.cpp b/SolderingPen_ESP32S2/UtilsEEPROM.cpp_ similarity index 100% rename from SolderingPen_ESP32S2/UtilsEEPROM.cpp rename to SolderingPen_ESP32S2/UtilsEEPROM.cpp_ diff --git a/SolderingPen_ESP32S2/UtilsEEPROM.h b/SolderingPen_ESP32S2/UtilsEEPROM.h_ similarity index 100% rename from SolderingPen_ESP32S2/UtilsEEPROM.h rename to SolderingPen_ESP32S2/UtilsEEPROM.h_ diff --git a/SolderingPen_ESP32S2/common.hpp b/SolderingPen_ESP32S2/common.hpp index c69cdc3..e3b28d2 100644 --- a/SolderingPen_ESP32S2/common.hpp +++ b/SolderingPen_ESP32S2/common.hpp @@ -10,7 +10,12 @@ (at your option) any later version. */ #pragma once +#if defined CUSTOM_CFG +# include CUSTOM_CFG +#warning "include custom cfg file" +#else #include "config.h" +#endif #include enum class ironState_t { diff --git a/SolderingPen_ESP32S2/config.h b/SolderingPen_ESP32S2/config.h index b58217f..50dbf1b 100644 --- a/SolderingPen_ESP32S2/config.h +++ b/SolderingPen_ESP32S2/config.h @@ -13,10 +13,6 @@ //typedef u8g2_uint_t u8g_uint_t; #define SCREEN_OFFSET 2 -// Type of rotary encoders / 旋转编码器的类型 -#define ROTARY_TYPE 0 // 0: 2 increments/step; 1: 4 increments/step (default) -#define BUTTON_DELAY 5 - // Pins #define SENSOR_PIN 1 // tip temperature sense 烙铁头温感 #define VIN_PIN 6 // input voltage sense 检测输入电压 @@ -26,13 +22,6 @@ #define BUTTON_DECR GPIO_NUM_4 // decrementer “-” push-button #define HEATER_PIN 5 // heater MOSFET PWM control 加热器MOSFET PWM控制 #define SH1107_RST_PIN 7 // display reset pin - -// Heater PWM parameters -#define HEATER_CHANNEL LEDC_CHANNEL_2 // PWM channel -#define HEATER_FREQ 200 // PWM frequency -#define HEATER_HIGHFREQ 1000 // PWM frequency for 20V/50% PWM mode -#define HEATER_RES LEDC_TIMER_8_BIT // PWM resolution - // CH224K USB PD chip pins connection // https://components101.com/sites/default/files/component_datasheet/WCH_CH224K_ENG.pdf #define PD_CFG_0 16 @@ -42,6 +31,12 @@ #define QC_DP_PIN 14 #define QC_DM_PIN 13 +// Heater PWM parameters +#define HEATER_CHANNEL LEDC_CHANNEL_2 // PWM channel +#define HEATER_FREQ 200 // PWM frequency +//#define HEATER_HIGHFREQ 1000 // PWM frequency for 20V/50% PWM mode +#define HEATER_RES LEDC_TIMER_8_BIT // PWM resolution + // Default temperature control value (recommended soldering temperature: 300~380°C) // 默认温度控制值(推荐焊接温度:300~380°C) #define TEMP_MIN 150 // 最小温度 diff --git a/SolderingPen_ESP32S2/heater.hpp b/SolderingPen_ESP32S2/heater.hpp index 2f51ecb..e7adf42 100644 --- a/SolderingPen_ESP32S2/heater.hpp +++ b/SolderingPen_ESP32S2/heater.hpp @@ -1,5 +1,5 @@ #pragma once -#include "config.h" +#include "common.hpp" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/ledc.h" diff --git a/SolderingPen_ESP32S2/hid.cpp b/SolderingPen_ESP32S2/hid.cpp index d45c992..c8f324e 100644 --- a/SolderingPen_ESP32S2/hid.cpp +++ b/SolderingPen_ESP32S2/hid.cpp @@ -473,11 +473,61 @@ void ViSet_ConfigurationMenu::_build_menu(){ LOGD(T_HID, println, "Build menu"); // create page with Settings options list - muiItem_id cfg_page = makePage(dictionary[D_Settings]); - -#define MAKE_ITEM(ITEM_TYPE, ARG, PAGE) { std::unique_ptr p = std::make_unique< ITEM_TYPE > ARG; addMuippItem(std::move(p), PAGE); } - - MAKE_ITEM( MuiItem_U8g2_PageTitle, (u8g2, nextIndex(), MAIN_MENU_FONT1 ), cfg_page); // u8g2_font_unifont_t_chinese3 + muiItemId page_mainmenu = makePage(dictionary[D_Settings]); + + // create page "Settings->Temperature" + muiItemId page_set_temp = makePage(menu_MainConfiguration.at(0), page_mainmenu); + + // create page "Settings->Timers" + muiItemId page_set_time = makePage(menu_MainConfiguration.at(1), page_mainmenu); + + // create page "Settings->Tip" + muiItemId page_set_tip = makePage(menu_MainConfiguration.at(2), page_mainmenu); + + // create page "Settings->Power" + muiItemId page_set_pwr = makePage(menu_MainConfiguration.at(3), page_mainmenu); + + // create page "Settings->Info" + muiItemId page_set_info = makePage(menu_MainConfiguration.at(4), page_mainmenu); + + + // #define MAKE_ITEM(ITEM_TYPE, ARG, PAGE) { std::unique_ptr p = std::make_unique< ITEM_TYPE > ARG; addMuippItem(std::move(p), PAGE); } + // MAKE_ITEM( MuiItem_U8g2_PageTitle, (u8g2, nextIndex(), MAIN_MENU_FONT1 ), page_mainmenu); // u8g2_font_unifont_t_chinese3 + + // generate idx for "Page title" item + muiItemId page_title_id = nextIndex(); + // create "Page title" item + MuiItem_pt p = std::make_shared< MuiItem_U8g2_PageTitle > (u8g2, page_title_id, MAIN_MENU_FONT1 ); + // add item to menu and bind it to "Main Settings" page + addMuippItem(std::move(p), page_mainmenu); + + // bind "Page title" item with "Settings->Temperature" page + addItemToPage(page_title_id, page_set_temp); + // bind "Page title" item with "Settings->Timers" page + addItemToPage(page_title_id, page_set_time); + // bind "Page title" item with "Settings->Tip" page + addItemToPage(page_title_id, page_set_tip); + // bind "Page title" item with "Settings->Power" page + addItemToPage(page_title_id, page_set_pwr); + // bind "Page title" item with "Settings->Info" page + addItemToPage(page_title_id, page_set_info); + + // generate idx for "Back Button" item + muiItemId bbuton_id = nextIndex(); + // create "Back Button" item + MuiItem_pt bb = std::make_shared< MuiItem_U8g2_BackButton > (u8g2, bbuton_id, dictionary[D_return], MAIN_MENU_FONT1, PAGE_BACK_BTN_X_OFFSET, PAGE_BACK_BTN_Y_OFFSET); + + // add item to menu and bind it to "Settings->Temperature" page + addMuippItem(std::move(bb), page_set_temp); + + // bind "Back Button" item with "Settings->Timers" page + addItemToPage(bbuton_id, page_set_time); + // bind "Back Button" item with "Settings->Tip" page + addItemToPage(bbuton_id, page_set_tip); + // bind "Back Button" item with "Settings->Power" page + addItemToPage(bbuton_id, page_set_pwr); + // bind "Back Button" item with "Settings->Info" page + addItemToPage(bbuton_id, page_set_info); //std::unique_ptr p = std::make_unique(u8g2, muipp.nextIndex(), u8g2_font_unifont_t_chinese3); //muipp.addMuippItem(std::move(p), cfg_page); @@ -492,16 +542,32 @@ void ViSet_ConfigurationMenu::_build_menu(){ //muipp.addMuippItem(std::move(p), cfg_page); // create and add to main page a list with settings selection options - muiItem_id menu_scroll_item = nextIndex(); - std::unique_ptr p = std::make_unique(u8g2, menu_scroll_item, + muiItemId menu_scroll_item = nextIndex(); +/* + std::unique_ptr p = std::make_unique(u8g2, menu_scroll_item, [](size_t index){ Serial.printf("idx:%u\n", index); return menu_MainConfiguration.at(index); }, // this lambda will feed localized strings to the MuiItem list builder class menu_MainConfiguration.size(), MAIN_MENU_Y_SHIFT, 3, // offset for each line of text and total number of lines in menu MAIN_MENU_FONT2, MAIN_MENU_FONT3, MAIN_MENU_X_OFFSET, MAIN_MENU_Y_OFFSET ); - addMuippItem(std::move(p), cfg_page); +*/ + MuiItem_U8g2_DynamicScrollList *menu = new MuiItem_U8g2_DynamicScrollList(u8g2, menu_scroll_item, + [](size_t index){ /* Serial.printf("idx:%u\n", index); */ return menu_MainConfiguration.at(index); }, // this lambda will feed localized strings to the MuiItem list builder class + [](){ return menu_MainConfiguration.size(); }, + nullptr, // action callback + MAIN_MENU_Y_SHIFT, 3, // offset for each line of text and total number of lines in menu + MAIN_MENU_X_OFFSET, MAIN_MENU_Y_OFFSET, // x,y cursor + MAIN_MENU_FONT2, MAIN_MENU_FONT3 + ); + + // set dynamic list will act as page selector + menu->listopts.page_selector = true; + // move menu object into MuiPP page + addMuippItem(menu, page_mainmenu); + // page_mainmenu + pageAutoSelect(page_mainmenu, menu_scroll_item); - menuStart(cfg_page, menu_scroll_item); + menuStart(page_mainmenu, menu_scroll_item); } @@ -520,12 +586,11 @@ void ViSet_ConfigurationMenu::drawScreen(){ mui_event ViSet_ConfigurationMenu::muipp_event(mui_event e) { //LOGD(T_HID, printf, "Got event for muipp lvl:1 e:%u\n", e.eid); + // forward those messages to muipp + auto reply = muiEvent(e); // any event will trigger screen refresh refresh_req = true; - - //return {}; - // forward those messages to muipp - return muiEvent(e); + return reply; } diff --git a/SolderingPen_ESP32S2/lang/i18n.h b/SolderingPen_ESP32S2/lang/i18n.h index 93fe2e6..e43d578 100644 --- a/SolderingPen_ESP32S2/lang/i18n.h +++ b/SolderingPen_ESP32S2/lang/i18n.h @@ -13,8 +13,7 @@ #include "MUIU8g2.h" -#define MENU_MAIN_CFG_SIZE 8 -//#define GP_DICT_SIZE 7 +#define MENU_MAIN_CFG_SIZE 6 /* // List of available translations @@ -29,7 +28,7 @@ enum lang_index : uint32_t { /** * Text-Dictionary Enums for language resources - * the order of enums must match with elements in dictionary + * the order of enums must match with elements in dictionary array */ enum dict_index { D_boost = (0), @@ -37,6 +36,7 @@ enum dict_index { D_heating, D_idle, D_notip, + D_return, D_Settings, D_set_t, D_standby, diff --git a/SolderingPen_ESP32S2/lang/lang_en_us.h b/SolderingPen_ESP32S2/lang/lang_en_us.h index 081c22f..0e6b744 100644 --- a/SolderingPen_ESP32S2/lang/lang_en_us.h +++ b/SolderingPen_ESP32S2/lang/lang_en_us.h @@ -17,8 +17,11 @@ #define MAIN_MENU_FONT1 u8g2_font_glasstown_nbp_t_all // 10x14 OK! #define MAIN_MENU_FONT2 u8g2_font_smart_patrol_nbp_tr +// 10x14 OK! +#define MAIN_MENU_FONT3 u8g2_font_smart_patrol_nbp_tr + // 12x17 -#define MAIN_MENU_FONT3 u8g2_font_shylock_nbp_t_all +//#define MAIN_MENU_FONT3 u8g2_font_shylock_nbp_t_all // 7x7 //#define MAIN_MENU_FONT3 u8g2_font_mercutio_sc_nbp_t_all @@ -26,6 +29,8 @@ #define MAIN_MENU_Y_OFFSET 12 #define MAIN_MENU_Y_SHIFT 15 +#define PAGE_BACK_BTN_X_OFFSET 95 +#define PAGE_BACK_BTN_Y_OFFSET 50 // EN-US namespace lang_en_us { @@ -38,27 +43,33 @@ static constexpr const char* T_Idle = "Idle"; static constexpr const char* T_NoTip = "No tip!"; // state display when tip is missing static constexpr const char* T_setT = "Set:"; // Target temperature on main screen static constexpr const char* T_standby = "Standby"; // state display in 'Standby' -static constexpr const char* T_return = " dictionary = { lang_en_us::T_Boost, lang_en_us::T_Error, lang_en_us::T_Heating, lang_en_us::T_Idle, lang_en_us::T_NoTip, + lang_en_us::T_return, lang_en_us::T_Settings, lang_en_us::T_setT, lang_en_us::T_standby @@ -69,9 +80,7 @@ static constexpr std::array menu_MainConfigura lang_en_us::T_TempSettings, lang_en_us::T_TimersSettings, lang_en_us::T_TipSettings, + lang_en_us::T_PowerSupply, lang_en_us::T_Information, - lang_en_us::T_Language, - lang_en_us::T_standby, - lang_en_us::T_Heating, lang_en_us::T_return }; diff --git a/SolderingPen_ESP32S2/main.cpp b/SolderingPen_ESP32S2/main.cpp index f5c6fa0..b5efcf2 100644 --- a/SolderingPen_ESP32S2/main.cpp +++ b/SolderingPen_ESP32S2/main.cpp @@ -8,7 +8,6 @@ #include "log.h" #include -#include "UtilsEEPROM.h" #ifdef CONFIG_TINYUSB_MSC_ENABLED #include "FirmwareMSC.h" #include "USB.h" @@ -95,9 +94,6 @@ void setup() { // event bus sniffer //evt::debug(); - // init EEPROM 从EEPROM获取默认值 - init_EEPROM(); - /* // if all 3 buttons are pressed on boot, reset EEPROM configuration to defaults // this does not makes much sense 'cause pressed gpio0 will put MCU into fash mode @@ -107,8 +103,6 @@ void setup() { } */ - // load configuration from eeprom - getEEPROM(); // fake readings for now int VoltageValue = 20; @@ -183,11 +177,6 @@ void beep() { // } } -// reads user settings from EEPROM; if EEPROM values are invalid, write defaults -// 从EEPROM读取用户设置;如果EEPROM值无效,则写入默认值 -void getEEPROM() { read_EEPROM(); } - - int32_t variance(int16_t a[]) { // Compute mean (average of elements)计算平均值(元素的平均值) diff --git a/SolderingPen_ESP32S2/sensors.hpp b/SolderingPen_ESP32S2/sensors.hpp index 8ffe5cd..45fe7df 100644 --- a/SolderingPen_ESP32S2/sensors.hpp +++ b/SolderingPen_ESP32S2/sensors.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "config.h" +#include "common.hpp" #include "SparkFun_LIS2DH12.h" // https://github.com/sparkfun/SparkFun_LIS2DH12_Arduino_Library #include "ESP32AnalogRead.h" // https://github.com/madhephaestus/ESP32AnalogRead #include "evtloop.hpp" From 9ccd9082b86a1dcd7b55498ca992da21cd9e70cc Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Sun, 12 May 2024 10:22:40 +0900 Subject: [PATCH 4/6] fixing bugs with menu navigation, testing checkboxes --- SolderingPen_ESP32S2/hid.cpp | 128 +++++++++++++++++-------- SolderingPen_ESP32S2/hid.hpp | 11 ++- SolderingPen_ESP32S2/lang/i18n.h | 3 +- SolderingPen_ESP32S2/lang/lang_en_us.h | 24 ++++- SolderingPen_ESP32S2/lang/lang_ru_ru.h | 1 + SolderingPen_ESP32S2/lang/lang_zh_cn.h | 3 + SolderingPen_ESP32S2/lang/lang_zh_tw.h | 2 + 7 files changed, 130 insertions(+), 42 deletions(-) diff --git a/SolderingPen_ESP32S2/hid.cpp b/SolderingPen_ESP32S2/hid.cpp index c8f324e..303c6e3 100644 --- a/SolderingPen_ESP32S2/hid.cpp +++ b/SolderingPen_ESP32S2/hid.cpp @@ -193,6 +193,7 @@ void IronHID::_switch_buttons_modes(uint32_t level){ switch(level){ // main working mode case 0 : + _menu.setMenuLevel(0); // switch button menu to Iron screen mode _btn.deactivateAll(); _btn.enableEvent(event_t::click); _btn.enableEvent(event_t::longPress); @@ -205,9 +206,11 @@ void IronHID::_switch_buttons_modes(uint32_t level){ // config menu navigation case 1 : + _menu.setMenuLevel(1); // switch button menu to mainConfig mode _btn.deactivateAll(); _btn.enableEvent(event_t::click); - // set encoder to control working temperature + _btn.enableEvent(event_t::longPress); + // set encoder to control cursor _encdr.reset(); //_encdr.setCounter(_temp.working, 5, TEMP_MIN, TEMP_MAX); //_encdr.setMultiplyFactor(2); @@ -227,7 +230,6 @@ void IronHID::_menu_0_main_mode(ESPButton::event_t e, const EventMsg* m){ // use longPress to enter configuration menu case event_t::longPress : - _menu.setMenuLevel(1); // switch button menu to mainConfig mode _switch_buttons_modes(1); // switch button events to navigate in menu switchScreen(vset_t::configMenu); break; @@ -247,20 +249,32 @@ void IronHID::_menu_1_config_navigation(ESPButton::event_t e, const EventMsg* m) LOGD(T_HID, printf, "Button Menu lvl:1 e:%d\n", e); switch(e){ // Use click event to send Select to MUI menu - case event_t::click : - viset->muipp_event( mui_event(mui_event_t::enter) ); + case event_t::click :{ + auto e = viset->muipp_event( mui_event(mui_event_t::enter) ); + // quit menu on event + if (e.eid == mui_event_t::quitMenu){ + switchScreen(vset_t::mainScreen); + _switch_buttons_modes(0); + } break; + } // use longPress to escape current item - case event_t::longPress : - viset->muipp_event( mui_event(mui_event_t::escape) ); + case event_t::longPress : { + auto e = viset->muipp_event( mui_event(mui_event_t::escape) ); + // quit menu on event + if (e.eid == mui_event_t::quitMenu){ + switchScreen(vset_t::mainScreen); + _switch_buttons_modes(0); + } break; + } } } -// ***** VisualSet - Main Screen ***** +// ***** VisualSet - Generic ***** VisualSet::VisualSet() { // subscribe to all events on a bus ESP_ERROR_CHECK(esp_event_handler_instance_register_with( @@ -294,6 +308,7 @@ void VisualSet::_event_picker(void* arg, esp_event_base_t base, int32_t id, void return reinterpret_cast(arg)->_evt_state(id, event_data); } +// ***** VisualSet - Main Screen ***** ViSet_MainScreen::ViSet_MainScreen() : VisualSet(){ // request working temperature from IronController EVT_POST(IRON_GET_EVT, e2int(iron_t::workTemp)); @@ -305,7 +320,7 @@ ViSet_MainScreen::ViSet_MainScreen() : VisualSet(){ void ViSet_MainScreen::drawScreen(){ u8g2.clearBuffer(); - u8g2.setFont(u8g2_font_unifont_t_chinese3); + u8g2.setFont(MAINSCREEN_FONT); //u8g2.setFont(PTS200_16); u8g2.setFontPosTop(); @@ -475,9 +490,6 @@ void ViSet_ConfigurationMenu::_build_menu(){ // create page with Settings options list muiItemId page_mainmenu = makePage(dictionary[D_Settings]); - // create page "Settings->Temperature" - muiItemId page_set_temp = makePage(menu_MainConfiguration.at(0), page_mainmenu); - // create page "Settings->Timers" muiItemId page_set_time = makePage(menu_MainConfiguration.at(1), page_mainmenu); @@ -501,8 +513,6 @@ void ViSet_ConfigurationMenu::_build_menu(){ // add item to menu and bind it to "Main Settings" page addMuippItem(std::move(p), page_mainmenu); - // bind "Page title" item with "Settings->Temperature" page - addItemToPage(page_title_id, page_set_temp); // bind "Page title" item with "Settings->Timers" page addItemToPage(page_title_id, page_set_time); // bind "Page title" item with "Settings->Tip" page @@ -512,16 +522,23 @@ void ViSet_ConfigurationMenu::_build_menu(){ // bind "Page title" item with "Settings->Info" page addItemToPage(page_title_id, page_set_info); +/* + // checkboxes + MuiItem_U8g2_CheckBox *box = new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), "Some box", true, nullptr, MAINSCREEN_FONT, 0, 25); + MuiItem_U8g2_CheckBox *box2 = new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), "Some box2", false, nullptr, MAINSCREEN_FONT, 0, 40); + addMuippItem(box, page_set_temp); + addMuippItem(box2, page_set_temp); +*/ + + // generate idx for "Back Button" item muiItemId bbuton_id = nextIndex(); // create "Back Button" item MuiItem_pt bb = std::make_shared< MuiItem_U8g2_BackButton > (u8g2, bbuton_id, dictionary[D_return], MAIN_MENU_FONT1, PAGE_BACK_BTN_X_OFFSET, PAGE_BACK_BTN_Y_OFFSET); - // add item to menu and bind it to "Settings->Temperature" page - addMuippItem(std::move(bb), page_set_temp); + // add item to menu and bind it to "Settings->Timers" page + addMuippItem(std::move(bb), page_set_time); - // bind "Back Button" item with "Settings->Timers" page - addItemToPage(bbuton_id, page_set_time); // bind "Back Button" item with "Settings->Tip" page addItemToPage(bbuton_id, page_set_tip); // bind "Back Button" item with "Settings->Power" page @@ -529,28 +546,10 @@ void ViSet_ConfigurationMenu::_build_menu(){ // bind "Back Button" item with "Settings->Info" page addItemToPage(bbuton_id, page_set_info); - //std::unique_ptr p = std::make_unique(u8g2, muipp.nextIndex(), u8g2_font_unifont_t_chinese3); - //muipp.addMuippItem(std::move(p), cfg_page); - - //MAKE_ITEM( MuiItem_U8g2_PageTitle, (u8g2, muipp.nextIndex(), MAIN_MENU_FONT2, 10, 15), cfg_page); - //std::unique_ptr p = std::make_unique(u8g2, muipp.nextIndex(), nullptr, 15, 15); - //muipp.addMuippItem(std::move(p), cfg_page); - - - //MAKE_ITEM( MuiItem_U8g2_PageTitle, (u8g2, muipp.nextIndex(), MAIN_MENU_FONT3, 10, 30), cfg_page); - //std::unique_ptr p = std::make_unique(u8g2, muipp.nextIndex(), nullptr, 30, 50); - //muipp.addMuippItem(std::move(p), cfg_page); - // create and add to main page a list with settings selection options muiItemId menu_scroll_item = nextIndex(); -/* - std::unique_ptr p = std::make_unique(u8g2, menu_scroll_item, - [](size_t index){ Serial.printf("idx:%u\n", index); return menu_MainConfiguration.at(index); }, // this lambda will feed localized strings to the MuiItem list builder class - menu_MainConfiguration.size(), - MAIN_MENU_Y_SHIFT, 3, // offset for each line of text and total number of lines in menu - MAIN_MENU_FONT2, MAIN_MENU_FONT3, MAIN_MENU_X_OFFSET, MAIN_MENU_Y_OFFSET - ); -*/ + + // Main menu dynamic scroll list MuiItem_U8g2_DynamicScrollList *menu = new MuiItem_U8g2_DynamicScrollList(u8g2, menu_scroll_item, [](size_t index){ /* Serial.printf("idx:%u\n", index); */ return menu_MainConfiguration.at(index); }, // this lambda will feed localized strings to the MuiItem list builder class [](){ return menu_MainConfiguration.size(); }, @@ -562,15 +561,67 @@ void ViSet_ConfigurationMenu::_build_menu(){ // set dynamic list will act as page selector menu->listopts.page_selector = true; + menu->listopts.back_on_last = true; // move menu object into MuiPP page addMuippItem(menu, page_mainmenu); // page_mainmenu pageAutoSelect(page_mainmenu, menu_scroll_item); - menuStart(page_mainmenu, menu_scroll_item); + + + + + + // start menu from page mainmenu + menuStart(page_mainmenu); +} + +void ViSet_ConfigurationMenu::_build_menu_temp_opts(muiItemId parent, muiItemId header, muiItemId footer){ + // create page "Settings->Temperature" + muiItemId page_set_temp = makePage(menu_MainConfiguration.at(0), parent); + + // bind "Page title" item with "Settings->Temperature" page + addItemToPage(header, page_set_temp); + + // create and add to main page a list with settings selection options + muiItemId menu_scroll_item = nextIndex(); + + // Main menu dynamic scroll list + MuiItem_U8g2_DynamicScrollList *menu = new MuiItem_U8g2_DynamicScrollList(u8g2, menu_scroll_item, + [](size_t index){ /* Serial.printf("idx:%u\n", index); */ return menu_TemperatureOpts.at(index); }, // this lambda will feed localized strings to the MuiItem list builder class + [](){ return menu_TemperatureOpts.size(); }, + nullptr, // action callback + MAIN_MENU_Y_SHIFT, 3, // offset for each line of text and total number of lines in menu + MAIN_MENU_X_OFFSET, MAIN_MENU_Y_OFFSET, // x,y cursor + MAIN_MENU_FONT2, MAIN_MENU_FONT3 + ); + + // set dynamic list will act as page selector + menu->listopts.page_selector = true; + menu->listopts.back_on_last = true; + // move menu object into MuiPP page + addMuippItem(menu, page_set_temp); + // page_mainmenu + pageAutoSelect(page_set_temp, menu_scroll_item); + + // create page "Temperature->Timers" + muiItemId page_T_Temp_SaveLastWrk = makePage(menu_TemperatureOpts.at(1), page_set_temp); + + MuiItem_U8g2_CheckBox *box = new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), "Some box", true, nullptr, MAINSCREEN_FONT, 0, 25); + addMuippItem(box, page_set_temp); + + // back button + addItemToPage(footer, page_set_temp); + + // add item to menu and bind it to "Settings->Temperature" page + //addMuippItem(std::move(bb), page_set_temp); + + + } + void ViSet_ConfigurationMenu::drawScreen(){ if (!refresh_req) return; @@ -597,6 +648,7 @@ mui_event ViSet_ConfigurationMenu::muipp_event(mui_event e) { // ***************************** // *** MUI entities +void _cb_save_wrk_temp(size_t index); diff --git a/SolderingPen_ESP32S2/hid.hpp b/SolderingPen_ESP32S2/hid.hpp index 23d9302..58778c1 100644 --- a/SolderingPen_ESP32S2/hid.hpp +++ b/SolderingPen_ESP32S2/hid.hpp @@ -90,6 +90,7 @@ class IronHID { // target temperature Temperatures _temp; + bool _save_wrk_temp; // encoder events executor, it works in cooperation with _menu object void _encoder_events(esp_event_base_t base, int32_t id, void* event_data); @@ -134,9 +135,14 @@ class IronHID { */ void switchScreen(vset_t v = vset_t::mainScreen); -}; +private: + + // MuiPP callback that sets save/not save work temperature flag + void _cb_save_wrk_temp(size_t index); + +}; /** * @brief Main Iron screen display @@ -180,6 +186,9 @@ class ViSet_ConfigurationMenu : public VisualSet, public MuiPlusPlus { void _build_menu(); + void _build_menu_temp_opts(muiItemId parent, muiItemId header, muiItemId footer); + + public: ViSet_ConfigurationMenu(); diff --git a/SolderingPen_ESP32S2/lang/i18n.h b/SolderingPen_ESP32S2/lang/i18n.h index e43d578..ea52877 100644 --- a/SolderingPen_ESP32S2/lang/i18n.h +++ b/SolderingPen_ESP32S2/lang/i18n.h @@ -14,7 +14,7 @@ #define MENU_MAIN_CFG_SIZE 6 - +#define MENU_TEMPERATURE_CFG_SIZE 4 /* // List of available translations enum lang_index : uint32_t { @@ -40,5 +40,6 @@ enum dict_index { D_Settings, D_set_t, D_standby, + D_Temp_SaveLastWrkDescr, D_____SIZE }; diff --git a/SolderingPen_ESP32S2/lang/lang_en_us.h b/SolderingPen_ESP32S2/lang/lang_en_us.h index 0e6b744..f6ff89f 100644 --- a/SolderingPen_ESP32S2/lang/lang_en_us.h +++ b/SolderingPen_ESP32S2/lang/lang_en_us.h @@ -13,6 +13,7 @@ #include #include "i18n.h" +#define MAINSCREEN_FONT u8g2_font_unifont_t_cyrillic // 12x16 wrong? small gothic font #define MAIN_MENU_FONT1 u8g2_font_glasstown_nbp_t_all // 10x14 OK! @@ -29,7 +30,7 @@ #define MAIN_MENU_Y_OFFSET 12 #define MAIN_MENU_Y_SHIFT 15 -#define PAGE_BACK_BTN_X_OFFSET 95 +#define PAGE_BACK_BTN_X_OFFSET 100 #define PAGE_BACK_BTN_Y_OFFSET 50 // EN-US @@ -45,6 +46,9 @@ static constexpr const char* T_setT = "Set:"; // Target temperatur static constexpr const char* T_standby = "Standby"; // state display in 'Standby' static constexpr const char* T_return = " dictionary = { lang_en_us::T_return, lang_en_us::T_Settings, lang_en_us::T_setT, - lang_en_us::T_standby + lang_en_us::T_standby, + lang_en_us::T_Temp_SaveLastWrkDescr }; // Main Configuration menu items @@ -84,3 +96,11 @@ static constexpr std::array menu_MainConfigura lang_en_us::T_Information, lang_en_us::T_return }; + +// Main Configuration menu items +static constexpr std::array menu_TemperatureOpts = { + lang_en_us::T_Temp_SaveLastWrk, + lang_en_us::T_TempDefltWrk, + lang_en_us::T_TempSleep, + lang_en_us::T_TempBoost +}; diff --git a/SolderingPen_ESP32S2/lang/lang_ru_ru.h b/SolderingPen_ESP32S2/lang/lang_ru_ru.h index b3d819f..6cc61a5 100644 --- a/SolderingPen_ESP32S2/lang/lang_ru_ru.h +++ b/SolderingPen_ESP32S2/lang/lang_ru_ru.h @@ -13,6 +13,7 @@ #include #include "i18n.h" +#define MAINSCREEN_FONT u8g2_font_unifont_t_cyrillic // 10x16 contains lat/cyr chars #define MAIN_MENU_FONT3 u8g2_font_mercutio_sc_nbp_t_all diff --git a/SolderingPen_ESP32S2/lang/lang_zh_cn.h b/SolderingPen_ESP32S2/lang/lang_zh_cn.h index 0ecfa6c..918ddce 100644 --- a/SolderingPen_ESP32S2/lang/lang_zh_cn.h +++ b/SolderingPen_ESP32S2/lang/lang_zh_cn.h @@ -14,6 +14,9 @@ #include "i18n.h" #include "lang_en_us.h" +#define MAINSCREEN_FONT u8g2_font_unifont_t_chinese3 + + // ZH-CN namespace lang_zh_cn { // ZH-CN diff --git a/SolderingPen_ESP32S2/lang/lang_zh_tw.h b/SolderingPen_ESP32S2/lang/lang_zh_tw.h index 240a1b7..2c27ebf 100644 --- a/SolderingPen_ESP32S2/lang/lang_zh_tw.h +++ b/SolderingPen_ESP32S2/lang/lang_zh_tw.h @@ -15,6 +15,8 @@ #include "lang_en_us.h" #include "lang_zh_cn.h" +#define MAINSCREEN_FONT u8g2_font_unifont_t_chinese3 + // ZH-TW namespace lang_zh_tw { // ZH-TW From aeb26731fb753cfec3ef2555015fa4efdc7e646b Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Mon, 13 May 2024 00:21:39 +0900 Subject: [PATCH 5/6] reworked HID and ViSet classes to be more flexible for dynamic menu sections --- SolderingPen_ESP32S2/config.h | 2 +- SolderingPen_ESP32S2/evtloop.cpp | 2 + SolderingPen_ESP32S2/evtloop.hpp | 1 + SolderingPen_ESP32S2/hid.cpp | 549 +++++++++++++------------ SolderingPen_ESP32S2/hid.hpp | 199 +++++---- SolderingPen_ESP32S2/lang/i18n.h | 2 +- SolderingPen_ESP32S2/lang/lang_en_us.h | 5 +- SolderingPen_ESP32S2/main.cpp | 2 +- 8 files changed, 421 insertions(+), 341 deletions(-) diff --git a/SolderingPen_ESP32S2/config.h b/SolderingPen_ESP32S2/config.h index 50dbf1b..08a1ce7 100644 --- a/SolderingPen_ESP32S2/config.h +++ b/SolderingPen_ESP32S2/config.h @@ -45,7 +45,7 @@ #define TEMP_DEFAULT 220 // 默认温度 #define TEMP_SLEEP 120 // 休眠温度 #define TEMP_BOOST 50 // 升温步进 -#define TEMP_STEP 10 // temperature change step / 旋转编码器温度变化步进 +#define TEMP_STEP 5 // temperature change step / 旋转编码器温度变化步进 #define POWER_LIMIT_15 170 // 功率限制 #define POWER_LIMIT_20 255 // 功率限制 #define POWER_LIMIT_20_2 127 // 功率限制 diff --git a/SolderingPen_ESP32S2/evtloop.cpp b/SolderingPen_ESP32S2/evtloop.cpp index bbeaf99..e6a0368 100644 --- a/SolderingPen_ESP32S2/evtloop.cpp +++ b/SolderingPen_ESP32S2/evtloop.cpp @@ -28,6 +28,8 @@ ESP_EVENT_DEFINE_BASE(IRON_SET_EVT); ESP_EVENT_DEFINE_BASE(IRON_GET_EVT); ESP_EVENT_DEFINE_BASE(IRON_NOTIFY); ESP_EVENT_DEFINE_BASE(IRON_STATE); +ESP_EVENT_DEFINE_BASE(IRON_VISET); + namespace evt { diff --git a/SolderingPen_ESP32S2/evtloop.hpp b/SolderingPen_ESP32S2/evtloop.hpp index 2427d93..cb401a3 100644 --- a/SolderingPen_ESP32S2/evtloop.hpp +++ b/SolderingPen_ESP32S2/evtloop.hpp @@ -23,6 +23,7 @@ ESP_EVENT_DECLARE_BASE(IRON_SET_EVT); // ESPIron setter Commands events ba ESP_EVENT_DECLARE_BASE(IRON_GET_EVT); // ESPIron getter Commands events base (in reply to this command, an IRON_STATE_EVT could be generated) ESP_EVENT_DECLARE_BASE(IRON_NOTIFY); // ESPIron notification events base (those events are published when some state or mode changes due to any commands or component's logic) ESP_EVENT_DECLARE_BASE(IRON_STATE); // ESPIron State publishing events base (those events are published on IRON_GET_EVT requests on demand) +ESP_EVENT_DECLARE_BASE(IRON_VISET); // ESPIron VisualSet HID events // cast enum to int template diff --git a/SolderingPen_ESP32S2/hid.cpp b/SolderingPen_ESP32S2/hid.cpp index 303c6e3..37f6829 100644 --- a/SolderingPen_ESP32S2/hid.cpp +++ b/SolderingPen_ESP32S2/hid.cpp @@ -52,10 +52,22 @@ U8G2_SH1107_64X128_F_HW_I2C u8g2(U8G2_R1, SH1107_RST_PIN); #define Y_OFFSET_VIN 50 -// shortcut alias +// shortcut type aliases using ESPButton::event_t; using evt::iron_t; +// ********************* +// *** IronHID *** + +IronHID::~IronHID(){ + if (_evt_viset_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_VISET, ESP_EVENT_ANY_ID, _evt_viset_handler); + _evt_viset_handler = nullptr; + } + + if (_tmr_display) + xTimerDelete( _tmr_display, portMAX_DELAY ); +} void IronHID::_init_screen(){ #ifndef NO_DISPLAY @@ -65,9 +77,18 @@ void IronHID::_init_screen(){ u8g2.enableUTF8Print(); #endif - // switch to default VisualSet - switchScreen(); + // subscribe to ViSet events + esp_event_handler_instance_register_with( + evt::get_hndlr(), + IRON_VISET, + ESP_EVENT_ANY_ID, + // VisualSet switching events + [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->switchViSet(static_cast(id)); }, + this, + &_evt_viset_handler + ); + // setup timer that will redraw screen periodically if (!_tmr_display){ _tmr_display = xTimerCreate("scrT", pdMS_TO_TICKS(TIMER_DISPLAY_REFRESH), @@ -89,193 +110,48 @@ void IronHID::_init_screen(){ } -void IronHID::switchScreen(vset_t v){ +void IronHID::switchViSet(viset_evt_t v){ // todo: do I need a locking mutex here? - LOGI(T_HID, printf, "switch screen to:%d\n", e2int(v)); + LOGI(T_HID, printf, "switch ViSet:%u\n", e2int(v)); switch(v){ - case vset_t::mainScreen : - viset = std::make_unique(); + case viset_evt_t::vsMainScreen : + viset = std::make_unique(_btn, _encdr); break; - case vset_t::configMenu : - viset = std::make_unique(); + case viset_evt_t::vsMainMenu : + viset = std::make_unique(_btn, _encdr); + break; + case viset_evt_t::vsMenuTemperature : + viset = std::make_unique(_btn, _encdr); break; - }; } +void IronHID::init(){ + // link our event loop with ESPButton's loop + ESPButton::set_event_loop_hndlr(evt::get_hndlr()); -// ********************* -// *** IronHID *** - -IronHID::IronHID() : _encdr(BUTTON_DECR, BUTTON_INCR, LOW), _btn(BUTTON_ACTION, LOW) { - _set_button_menu_callbacks(); -} + // create default ViSet with Main Work screen + switchViSet(viset_evt_t::vsMainScreen); -void IronHID::init(const Temperatures& t){ // initialize screen _init_screen(); - // link our event loop with ESPButton - ESPButton::set_event_loop_hndlr(evt::get_hndlr()); - - // subscribe to button events - if (!_btn_evt_handler){ - ESP_ERROR_CHECK(esp_event_handler_instance_register_with( - evt::get_hndlr(), - EBTN_EVENTS, ESP_EVENT_ANY_ID, - [](void* self, esp_event_base_t base, int32_t id, void* data) { - // pick action button events and pass it to _menu member - LOGD(T_HID, printf, "btn event:%d, gpio:%d\n", id, reinterpret_cast(data)->gpio); - if (reinterpret_cast(data)->gpio == BUTTON_ACTION) - static_cast(self)->_menu.handleEvent(ESPButton::int2event_t(id), reinterpret_cast(data)); - }, - this, &_btn_evt_handler) - ); - } - - if (!_enc_evt_handler){ - ESP_ERROR_CHECK(esp_event_handler_instance_register_with( - evt::get_hndlr(), - EBTN_ENC_EVENTS, ESP_EVENT_ANY_ID, - [](void* self, esp_event_base_t base, int32_t id, void* data) { - LOGD(T_HID, printf, "enc event:%d, cnt:%d\n", id, reinterpret_cast(data)->cntr); - // pick encoder events and pass it to _menu member - static_cast(self)->_encoder_events(base, id, data); - }, - this, &_enc_evt_handler) - ); - } - - // get initial temperatures - _temp = t; - // enable middle button _btn.enable(); // enable 'encoder' buttons _encdr.begin(); - // set level 0 for button and encoder buttons - _switch_buttons_modes(0); -} - -void IronHID::_set_button_menu_callbacks(){ - // actions for middle button in main mode - _menu.assign(BUTTON_ACTION, 0, [this](event_t e, const EventMsg* m){ _menu_0_main_mode(e, m); }); - _menu.assign(BUTTON_ACTION, 1, [this](event_t e, const EventMsg* m){ _menu_1_config_navigation(e, m); }); - } -// encoder events executor, it works in cooperation with _menu object -void IronHID::_encoder_events(esp_event_base_t base, int32_t id, void* event_data){ - LOGD(T_HID, printf, "_encoder_events:%d, cnt:%d\n", id, reinterpret_cast(event_data)->cntr); - switch(_menu.getMenuLevel()){ - - // main Iron screen mode - encoder controls Iron working temperature - case 0 : { - _temp.working = reinterpret_cast(event_data)->cntr; - EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::workTemp), &_temp.working, sizeof(_temp.working)); - break; - } - - // configuration Menu - encoder sends commands to MuiPP sink - case 1 : { - // we do not need counter value here (for now), just figure out if it was increment or decrement via gpio which triggered and event - if (reinterpret_cast(event_data)->gpio == BUTTON_INCR){ - viset->muipp_event( {mui_event_t::moveDown, 0, nullptr} ); - } else { - viset->muipp_event( {mui_event_t::moveUp, 0, nullptr} ); - } - break; - } - } -} - -void IronHID::_switch_buttons_modes(uint32_t level){ - switch(level){ - // main working mode - case 0 : - _menu.setMenuLevel(0); // switch button menu to Iron screen mode - _btn.deactivateAll(); - _btn.enableEvent(event_t::click); - _btn.enableEvent(event_t::longPress); - _btn.enableEvent(event_t::multiClick); - // set encoder to control working temperature - _encdr.reset(); - _encdr.setCounter(_temp.working, 5, TEMP_MIN, TEMP_MAX); - _encdr.setMultiplyFactor(2); - break; - - // config menu navigation - case 1 : - _menu.setMenuLevel(1); // switch button menu to mainConfig mode - _btn.deactivateAll(); - _btn.enableEvent(event_t::click); - _btn.enableEvent(event_t::longPress); - // set encoder to control cursor - _encdr.reset(); - //_encdr.setCounter(_temp.working, 5, TEMP_MIN, TEMP_MAX); - //_encdr.setMultiplyFactor(2); - break; - - } -} - -// actions for middle button when iron is main working mode -void IronHID::_menu_0_main_mode(ESPButton::event_t e, const EventMsg* m){ - LOGD(T_HID, printf, "Button Menu lvl:0 e:%d\n", e); - switch(e){ - // Use click event to toggle iron working mode on/off - case event_t::click : - EVT_POST(IRON_SET_EVT, e2int(iron_t::workModeToggle)); - break; - - // use longPress to enter configuration menu - case event_t::longPress : - _switch_buttons_modes(1); // switch button events to navigate in menu - switchScreen(vset_t::configMenu); - break; - - // pick multiClicks - case event_t::multiClick : - // doubleclick to toggle boost mode - if (m->cntr == 2) - EVT_POST(IRON_SET_EVT, e2int(iron_t::boostModeToggle)); - break; - - } -} - -// actions for middle button in main Configuration menu -void IronHID::_menu_1_config_navigation(ESPButton::event_t e, const EventMsg* m){ - LOGD(T_HID, printf, "Button Menu lvl:1 e:%d\n", e); - switch(e){ - // Use click event to send Select to MUI menu - case event_t::click :{ - auto e = viset->muipp_event( mui_event(mui_event_t::enter) ); - // quit menu on event - if (e.eid == mui_event_t::quitMenu){ - switchScreen(vset_t::mainScreen); - _switch_buttons_modes(0); - } - break; - } - - // use longPress to escape current item - case event_t::longPress : { - auto e = viset->muipp_event( mui_event(mui_event_t::escape) ); - // quit menu on event - if (e.eid == mui_event_t::quitMenu){ - switchScreen(vset_t::mainScreen); - _switch_buttons_modes(0); - } - break; - } - } -} +// ***** VisualSet - Generic ***** +VisualSet::VisualSet(GPIOButton &button, PseudoRotaryEncoder &encoder) : btn(button), encdr(encoder) { + // subscribe to button events + esp_event_handler_instance_register_with(evt::get_hndlr(), EBTN_EVENTS, ESP_EVENT_ANY_ID, VisualSet::_event_picker, this, &_evt_btn_handler); + // subscribe to encoder events + esp_event_handler_instance_register_with(evt::get_hndlr(), EBTN_ENC_EVENTS, ESP_EVENT_ANY_ID, VisualSet::_event_picker, this, &_evt_enc_handler); -// ***** VisualSet - Generic ***** -VisualSet::VisualSet() { +/* // subscribe to all events on a bus ESP_ERROR_CHECK(esp_event_handler_instance_register_with( evt::get_hndlr(), @@ -283,38 +159,91 @@ VisualSet::VisualSet() { VisualSet::_event_picker, this, &_evt_handler) ); +*/ } VisualSet::~VisualSet(){ // unsubscribe from an event bus - if (_evt_handler){ - esp_event_handler_instance_unregister_with(evt::get_hndlr(), ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, _evt_handler); - _evt_handler = nullptr; + if (_evt_btn_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), EBTN_EVENTS, ESP_EVENT_ANY_ID, _evt_btn_handler); + _evt_btn_handler = nullptr; + } + if (_evt_enc_handler){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), EBTN_ENC_EVENTS, ESP_EVENT_ANY_ID, _evt_enc_handler); + _evt_enc_handler = nullptr; } + } -void VisualSet::_event_picker(void* arg, esp_event_base_t base, int32_t id, void* event_data){ +void VisualSet::_event_picker(void* arg, esp_event_base_t base, int32_t id, void* data){ //LOGV(printf, "VisualSet::_event_picker %s:%d\n", base, id); - if (base == SENSOR_DATA) - return reinterpret_cast(arg)->_evt_sensor(id, event_data); - if (base == IRON_NOTIFY) - return reinterpret_cast(arg)->_evt_notify(id, event_data); - - if (base == IRON_SET_EVT) - return reinterpret_cast(arg)->_evt_cmd(id, event_data); + // button events + if (base == EBTN_EVENTS){ + // pick events only for "enter" gpio putton + if (reinterpret_cast(data)->gpio == BUTTON_ACTION){ + LOGV(T_HID, printf, "btn event:%d, gpio:%d\n", id, reinterpret_cast(data)->gpio); + static_cast(arg)->_evt_button(ESPButton::int2event_t(id), reinterpret_cast(data)); + //static_cast(self)->_btn_menu.handleEvent(ESPButton::int2event_t(id), reinterpret_cast(data)); + } + return; + } - if (base == IRON_STATE) - return reinterpret_cast(arg)->_evt_state(id, event_data); + // encoder events + if (base == EBTN_ENC_EVENTS){ + LOGV(T_HID, printf, "enc event:%d, cnt:%d\n", id, reinterpret_cast(data)->cntr); + // pick encoder events and pass it to _menu member + static_cast(arg)->_evt_encoder(ESPButton::int2event_t(id), reinterpret_cast(data)); + return; + } } + // ***** VisualSet - Main Screen ***** -ViSet_MainScreen::ViSet_MainScreen() : VisualSet(){ +ViSet_MainScreen::ViSet_MainScreen(GPIOButton &button, PseudoRotaryEncoder &encoder) : VisualSet(button, encoder) { + //LOGV(printf, "ViSet_MainScreen::_event_picker %s:%d\n", base, id); + + // subscribe to sensor events + esp_event_handler_instance_register_with(evt::get_hndlr(), SENSOR_DATA, ESP_EVENT_ANY_ID, + [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->_evt_sensor(id, data); }, + this, &_evt_snsr_handler); + // subscribe to notify events + esp_event_handler_instance_register_with(evt::get_hndlr(), IRON_NOTIFY, ESP_EVENT_ANY_ID, + [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->_evt_notify(id, data); }, + this, &_evt_ntfy_handler); + // subscribe to notify events + esp_event_handler_instance_register_with(evt::get_hndlr(), IRON_SET_EVT, ESP_EVENT_ANY_ID, + [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->_evt_cmd(id, data); }, + this, &_evt_set_handler); + // subscribe to notify events + esp_event_handler_instance_register_with(evt::get_hndlr(), IRON_STATE, ESP_EVENT_ANY_ID, + [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->_evt_state(id, data); }, + this, &_evt_state_handler); + + + // configure button and encoder + btn.deactivateAll(); + btn.enableEvent(event_t::click); + btn.enableEvent(event_t::longPress); + btn.enableEvent(event_t::multiClick); + // set encoder to control working temperature + encdr.reset(); + encdr.setCounter(TEMP_DEFAULT, TEMP_STEP, TEMP_MIN, TEMP_MAX); + encdr.setMultiplyFactor(2); + // request working temperature from IronController EVT_POST(IRON_GET_EVT, e2int(iron_t::workTemp)); +} - // the screen will redraw at first event from sensors - //_drawMainScreen(); +ViSet_MainScreen::~ViSet_MainScreen(){ + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_VISET, ESP_EVENT_ANY_ID, _evt_snsr_handler); + _evt_snsr_handler = nullptr; + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_VISET, ESP_EVENT_ANY_ID, _evt_ntfy_handler); + _evt_ntfy_handler = nullptr; + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_VISET, ESP_EVENT_ANY_ID, _evt_set_handler); + _evt_set_handler = nullptr; + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_VISET, ESP_EVENT_ANY_ID, _evt_state_handler); + _evt_state_handler = nullptr; } void ViSet_MainScreen::drawScreen(){ @@ -436,11 +365,7 @@ void ViSet_MainScreen::_evt_sensor(int32_t id, void* data){ case evt::iron_t::acceltemp : _sns_temp = *reinterpret_cast(data); break; - - default: - return; } - //_drawMainScreen(); } @@ -465,6 +390,7 @@ void ViSet_MainScreen::_evt_cmd(int32_t id, void* data){ switch(static_cast(id)){ case evt::iron_t::workTemp : _temp.working = *reinterpret_cast(data); + encdr.setCounter(_temp.working, TEMP_STEP, TEMP_MIN, TEMP_MAX); break; } } @@ -473,45 +399,140 @@ void ViSet_MainScreen::_evt_state(int32_t id, void* data){ switch(static_cast(id)){ case evt::iron_t::workTemp : _temp.working = *reinterpret_cast(data); + encdr.setCounter(_temp.working, TEMP_STEP, TEMP_MIN, TEMP_MAX); break; } } +void ViSet_MainScreen::_evt_button(ESPButton::event_t e, const EventMsg* m){ + // actions for middle button when iron is main working mode + LOGD(T_HID, printf, "Button main screen e:%d, cnt: %d\n", e, m->cntr); + switch(e){ + // Use click event to toggle iron working mode on/off + case event_t::click : + EVT_POST(IRON_SET_EVT, e2int(iron_t::workModeToggle)); + break; + + // use longPress to enter configuration menu + case event_t::longPress : + EVT_POST(IRON_VISET, e2int(viset_evt_t::vsMainMenu)); + //switchScreen(vset_t::configMenu); + break; + + // pick multiClicks + case event_t::multiClick : + // doubleclick to toggle boost mode + if (m->cntr == 2) + EVT_POST(IRON_SET_EVT, e2int(iron_t::boostModeToggle)); + break; + } +} + +void ViSet_MainScreen::_evt_encoder(ESPButton::event_t e, const EventMsg* m){ + // main Iron screen mode - encoder controls Iron working temperature + _temp.working = m->cntr; // reinterpret_cast(event_data)->cntr; + EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::workTemp), &_temp.working, sizeof(_temp.working)); +} + -// ***** VisualSet - ConfigurationMenu ***** -ViSet_ConfigurationMenu::ViSet_ConfigurationMenu(){ - _build_menu(); +// ***** MuiMenu Generics ***** + +MuiMenu::MuiMenu(GPIOButton &button, PseudoRotaryEncoder &encoder) : VisualSet(button, encoder) { + // set buttons and encoder + btn.deactivateAll(); + btn.enableEvent(event_t::click); + btn.enableEvent(event_t::longPress); + // set encoder to control cursor + encdr.reset(); + //_encdr.setCounter(_temp.working, 5, TEMP_MIN, TEMP_MAX); + //_encdr.setMultiplyFactor(2); }; -void ViSet_ConfigurationMenu::_build_menu(){ - LOGD(T_HID, println, "Build menu"); +// actions for middle button in main Configuration menu +void MuiMenu::_evt_button(ESPButton::event_t e, const EventMsg* m){ + LOGD(T_HID, printf, "_evt_button:%u, cnt:%d\n", e2int(e), m->cntr); + switch(e){ + // Use click event to send 'enter/ok' to MUI menu + case event_t::click :{ + auto e = muiEvent( mui_event(mui_event_t::enter) ); + // signal switch to Main Work Screen in quitMenu event received + if (e.eid == mui_event_t::quitMenu){ + EVT_POST(IRON_VISET, e2int(viset_evt_t::vsMainScreen)); + } + break; + } + + // use longPress to escape current item + case event_t::longPress : { + auto e = muiEvent( mui_event(mui_event_t::escape) ); + // signal switch to Main Work Screen in quitMenu event received + if (e.eid == mui_event_t::quitMenu){ + EVT_POST(IRON_VISET, e2int(viset_evt_t::vsMainScreen)); + } + break; + } + } + + // redraw screen + _rr = true; +} + +// encoder events picker +void MuiMenu::_evt_encoder(ESPButton::event_t e, const EventMsg* m){ + LOGD(T_HID, printf, "_evt_encoder:%u, cnt:%d\n", e2int(e), m->cntr); + // I do not need counter value here (for now), just figure out if it was increment or decrement via gpio which triggered and event + if (m->gpio == BUTTON_INCR){ + muiEvent( {mui_event_t::moveDown, 0, nullptr} ); + } else { + muiEvent( {mui_event_t::moveUp, 0, nullptr} ); + } + + // redraw screen + _rr = true; +} + +void MuiMenu::drawScreen(){ + if (!_rr) return; + + Serial.printf("st:%lu\n", millis()); + u8g2.clearBuffer(); + // call Mui renderer + render(); + u8g2.sendBuffer(); + Serial.printf("en:%lu\n", millis()); + + _rr = false; +} + + +// ************************************** +// *** Main Configuration Menu *** + +void ViSet_MainMenu::_buildMenu(){ + LOGD(T_HID, println, "Build Main Menu"); // create page with Settings options list - muiItemId page_mainmenu = makePage(dictionary[D_Settings]); + muiItemId root_page = makePage(dictionary[D_Settings]); // create page "Settings->Timers" - muiItemId page_set_time = makePage(menu_MainConfiguration.at(1), page_mainmenu); + muiItemId page_set_time = makePage(menu_MainConfiguration.at(1), root_page); // create page "Settings->Tip" - muiItemId page_set_tip = makePage(menu_MainConfiguration.at(2), page_mainmenu); + muiItemId page_set_tip = makePage(menu_MainConfiguration.at(2), root_page); // create page "Settings->Power" - muiItemId page_set_pwr = makePage(menu_MainConfiguration.at(3), page_mainmenu); + muiItemId page_set_pwr = makePage(menu_MainConfiguration.at(3), root_page); // create page "Settings->Info" - muiItemId page_set_info = makePage(menu_MainConfiguration.at(4), page_mainmenu); - - - // #define MAKE_ITEM(ITEM_TYPE, ARG, PAGE) { std::unique_ptr p = std::make_unique< ITEM_TYPE > ARG; addMuippItem(std::move(p), PAGE); } - // MAKE_ITEM( MuiItem_U8g2_PageTitle, (u8g2, nextIndex(), MAIN_MENU_FONT1 ), page_mainmenu); // u8g2_font_unifont_t_chinese3 + muiItemId page_set_info = makePage(menu_MainConfiguration.at(4), root_page); // generate idx for "Page title" item muiItemId page_title_id = nextIndex(); // create "Page title" item MuiItem_pt p = std::make_shared< MuiItem_U8g2_PageTitle > (u8g2, page_title_id, MAIN_MENU_FONT1 ); // add item to menu and bind it to "Main Settings" page - addMuippItem(std::move(p), page_mainmenu); + addMuippItem(std::move(p), root_page); // bind "Page title" item with "Settings->Timers" page addItemToPage(page_title_id, page_set_time); @@ -526,8 +547,8 @@ void ViSet_ConfigurationMenu::_build_menu(){ // checkboxes MuiItem_U8g2_CheckBox *box = new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), "Some box", true, nullptr, MAINSCREEN_FONT, 0, 25); MuiItem_U8g2_CheckBox *box2 = new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), "Some box2", false, nullptr, MAINSCREEN_FONT, 0, 40); - addMuippItem(box, page_set_temp); - addMuippItem(box2, page_set_temp); + addMuippItem(box, root_page); + addMuippItem(box2, root_page); */ @@ -547,108 +568,110 @@ void ViSet_ConfigurationMenu::_build_menu(){ addItemToPage(bbuton_id, page_set_info); // create and add to main page a list with settings selection options - muiItemId menu_scroll_item = nextIndex(); + muiItemId scroll_list_id = nextIndex(); // Main menu dynamic scroll list - MuiItem_U8g2_DynamicScrollList *menu = new MuiItem_U8g2_DynamicScrollList(u8g2, menu_scroll_item, + MuiItem_U8g2_DynamicScrollList *menu = new MuiItem_U8g2_DynamicScrollList(u8g2, scroll_list_id, [](size_t index){ /* Serial.printf("idx:%u\n", index); */ return menu_MainConfiguration.at(index); }, // this lambda will feed localized strings to the MuiItem list builder class - [](){ return menu_MainConfiguration.size(); }, - nullptr, // action callback + [](){ return menu_MainConfiguration.size(); }, // list size callback + [this](size_t idx){ _submenu_selector(idx); }, // action callback MAIN_MENU_Y_SHIFT, 3, // offset for each line of text and total number of lines in menu MAIN_MENU_X_OFFSET, MAIN_MENU_Y_OFFSET, // x,y cursor - MAIN_MENU_FONT2, MAIN_MENU_FONT3 + MAIN_MENU_FONT2, NULL ); // set dynamic list will act as page selector - menu->listopts.page_selector = true; + //menu->listopts.page_selector = true; menu->listopts.back_on_last = true; // move menu object into MuiPP page - addMuippItem(menu, page_mainmenu); - // page_mainmenu - pageAutoSelect(page_mainmenu, menu_scroll_item); - - + addMuippItem(menu, root_page); + // root_page + pageAutoSelect(root_page, scroll_list_id); + // start menu from page mainmenu + menuStart(root_page); +} +void ViSet_MainMenu::_submenu_selector(size_t index){ + LOGD(T_HID, printf, "_submenu_selector:%u\n", index); + switch (index){ + case 0 : // temp settings + EVT_POST(IRON_VISET, e2int(viset_evt_t::vsMenuTemperature)); + break; + } +} - // start menu from page mainmenu - menuStart(page_mainmenu); -} +// ************************************** +// *** Temperature Control Menu *** -void ViSet_ConfigurationMenu::_build_menu_temp_opts(muiItemId parent, muiItemId header, muiItemId footer){ +void ViSet_TemperatureSetup::_buildMenu(){ // create page "Settings->Temperature" - muiItemId page_set_temp = makePage(menu_MainConfiguration.at(0), parent); + muiItemId root_page = makePage(menu_MainConfiguration.at(0)); - // bind "Page title" item with "Settings->Temperature" page - addItemToPage(header, page_set_temp); + // generate idx for "Page title" item + muiItemId page_title_id = nextIndex(); + // create "Page title" item + MuiItem_pt p = std::make_shared< MuiItem_U8g2_PageTitle > (u8g2, page_title_id, MAIN_MENU_FONT1 ); + // add item to menu and bind it to "Main Settings" page + addMuippItem(std::move(p), root_page); // create and add to main page a list with settings selection options - muiItemId menu_scroll_item = nextIndex(); + muiItemId scroll_list_id = nextIndex(); - // Main menu dynamic scroll list - MuiItem_U8g2_DynamicScrollList *menu = new MuiItem_U8g2_DynamicScrollList(u8g2, menu_scroll_item, + // Temperature menu dynamic scroll list + MuiItem_U8g2_DynamicScrollList *list = new MuiItem_U8g2_DynamicScrollList(u8g2, scroll_list_id, [](size_t index){ /* Serial.printf("idx:%u\n", index); */ return menu_TemperatureOpts.at(index); }, // this lambda will feed localized strings to the MuiItem list builder class [](){ return menu_TemperatureOpts.size(); }, nullptr, // action callback MAIN_MENU_Y_SHIFT, 3, // offset for each line of text and total number of lines in menu MAIN_MENU_X_OFFSET, MAIN_MENU_Y_OFFSET, // x,y cursor - MAIN_MENU_FONT2, MAIN_MENU_FONT3 + MAIN_MENU_FONT3, MAIN_MENU_FONT3 ); - // set dynamic list will act as page selector - menu->listopts.page_selector = true; - menu->listopts.back_on_last = true; + // dynamic list will act as page selector + list->listopts.page_selector = true; + list->listopts.back_on_last = true; // move menu object into MuiPP page - addMuippItem(menu, page_set_temp); - // page_mainmenu - pageAutoSelect(page_set_temp, menu_scroll_item); - - // create page "Temperature->Timers" - muiItemId page_T_Temp_SaveLastWrk = makePage(menu_TemperatureOpts.at(1), page_set_temp); - - MuiItem_U8g2_CheckBox *box = new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), "Some box", true, nullptr, MAINSCREEN_FONT, 0, 25); - addMuippItem(box, page_set_temp); - - // back button - addItemToPage(footer, page_set_temp); + addMuippItem(list, root_page); + // root_page + pageAutoSelect(root_page, scroll_list_id); - // add item to menu and bind it to "Settings->Temperature" page - //addMuippItem(std::move(bb), page_set_temp); + // page "save work temp" + muiItemId page_savewrkT = makePage(menu_TemperatureOpts.at(0), root_page); + // page title element + addItemToPage(page_title_id, page_savewrkT); + // checkbox + MuiItem_U8g2_CheckBox *box = new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), "Some box with very long text here...", true, nullptr, MAINSCREEN_FONT, 0, 25); + addMuippItem(box, page_savewrkT); -} + // generate idx for "Back Button" item + muiItemId bbuton_id = nextIndex(); + // create "Back Button" item + MuiItem_pt bb = std::make_shared< MuiItem_U8g2_BackButton > (u8g2, bbuton_id, dictionary[D_return], MAIN_MENU_FONT1, PAGE_BACK_BTN_X_OFFSET, PAGE_BACK_BTN_Y_OFFSET); + // add item and bind it to "save work temp" page + addMuippItem(std::move(bb), page_savewrkT); -void ViSet_ConfigurationMenu::drawScreen(){ - if (!refresh_req) return; + // back button + //addItemToPage(footer, root_page); - Serial.printf("st:%lu\n", millis()); - u8g2.clearBuffer(); - render(); - u8g2.sendBuffer(); - Serial.printf("en:%lu\n", millis()); + // add item to menu and bind it to "Settings->Temperature" page + //addMuippItem(std::move(bb), root_page); - refresh_req = false; + // start menu from root page + menuStart(root_page); } -mui_event ViSet_ConfigurationMenu::muipp_event(mui_event e) { - //LOGD(T_HID, printf, "Got event for muipp lvl:1 e:%u\n", e.eid); - // forward those messages to muipp - auto reply = muiEvent(e); - // any event will trigger screen refresh - refresh_req = true; - return reply; -} // ***************************** // *** MUI entities -void _cb_save_wrk_temp(size_t index); diff --git a/SolderingPen_ESP32S2/hid.hpp b/SolderingPen_ESP32S2/hid.hpp index 58778c1..420c07a 100644 --- a/SolderingPen_ESP32S2/hid.hpp +++ b/SolderingPen_ESP32S2/hid.hpp @@ -15,6 +15,19 @@ #include "espasyncbutton.hpp" #include "muipp_u8g2.h" + +/** + * @brief controls feedback events returned from VisualSet instance to IronHID class + * + */ +enum class viset_evt_t { + noop, // no operation + vsMainScreen, // switch to Main work screen + vsMainMenu, // switch to Main Menu + vsMenuTemperature, // switch to Temperature setup menu + quitCfgMenu // MuiPP menu exist +}; + /** * @brief A generic screen object instance * abstract class that represents some screen information, @@ -23,32 +36,40 @@ */ class VisualSet { - esp_event_handler_instance_t _evt_handler = nullptr; + // action button event handler + esp_event_handler_instance_t _evt_btn_handler = nullptr; + + // encoder event handler + esp_event_handler_instance_t _evt_enc_handler = nullptr; // event dispatcher static void _event_picker(void* arg, esp_event_base_t base, int32_t id, void* event_data); protected: - // flag that shows a screen must be refreshed - bool refresh_req{true}; - // sensor events picker - virtual void _evt_sensor(int32_t id, void* event_data){}; - // notifications - virtual void _evt_notify(int32_t id, void* event_data){}; - // commands - virtual void _evt_cmd(int32_t id, void* data){}; - // commands - virtual void _evt_state(int32_t id, void* data){}; + /** + * @brief reference to pseudo-encoder object + * ViSet can adjust it's properties to fit proper control + * + */ + PseudoRotaryEncoder &encdr; + /** + * @brief reference to button object + * ViSet can adjust it's properties to fit proper control + * + */ + GPIOButton &btn; + + // button events picker + virtual void _evt_button(ESPButton::event_t e, const EventMsg* m){}; + // encoder events picker + virtual void _evt_encoder(ESPButton::event_t e, const EventMsg* m){}; public: - VisualSet(); + VisualSet(GPIOButton &button, PseudoRotaryEncoder &encoder); virtual ~VisualSet(); - // MuiPlusPlus event sink, might be used in derrived classes - virtual mui_event muipp_event(mui_event e){ return {}; }; - // draw on screen information virtual void drawScreen() = 0; }; @@ -59,47 +80,21 @@ class VisualSet { * */ class IronHID { - // a set of availbale "screens" - enum class vset_t { - mainScreen = 0, - configMenu - }; // Display object - an instance of visual set that represents displayed info on a screen std::unique_ptr viset; + // screen redraw timer TimerHandle_t _tmr_display = nullptr; - // flag that shows a screen must be refreshed - //bool refresh_req{true}; - // Two button pseudo-encoder - PseudoRotaryEncoder _encdr; + // event handler + esp_event_handler_instance_t _evt_viset_handler = nullptr; // action button GPIOButton _btn; - // action button menu - ButtonCallbackMenu _menu; - - // action button event handler - esp_event_handler_instance_t _btn_evt_handler = nullptr; - - // encoder event handler - esp_event_handler_instance_t _enc_evt_handler = nullptr; - - // target temperature - Temperatures _temp; - - bool _save_wrk_temp; - - // encoder events executor, it works in cooperation with _menu object - void _encoder_events(esp_event_base_t base, int32_t id, void* event_data); - - // init action button menu - void _set_button_menu_callbacks(); - - // switch between different menu modes for action button - void _switch_buttons_modes(uint32_t level); + // Two button pseudo-encoder + PseudoRotaryEncoder _encdr; /** * @brief initialize screen @@ -109,39 +104,34 @@ class IronHID { void _init_screen(); /** - * @brief button actions when iron is main working mode + * @brief button action handlers when iron is in menu configuration * */ - void _menu_0_main_mode(ESPButton::event_t e, const EventMsg* m); - - void _menu_1_config_navigation(ESPButton::event_t e, const EventMsg* m); + //void _button_config_menu(ESPButton::event_t e, const EventMsg* m); public: // c-tor - IronHID(); - + IronHID() : _encdr(BUTTON_DECR, BUTTON_INCR, LOW), _btn(BUTTON_ACTION, LOW) {}; + // d-tor + ~IronHID(); /** * @brief initialize HID, * attach to button events, init display class, etc... * */ - void init(const Temperatures& t); + void init(); /** * @brief switch to another instance of VisualSet's object * this method will spawn a new instance of screen renderer depending on requested paramenter * */ - void switchScreen(vset_t v = vset_t::mainScreen); - + void switchViSet(viset_evt_t v); private: - // MuiPP callback that sets save/not save work temperature flag - void _cb_save_wrk_temp(size_t index); - }; /** @@ -158,44 +148,107 @@ class ViSet_MainScreen : public VisualSet { // input voltage uint32_t _vin{0}; - // renders Main working screen - void drawScreen() override; + esp_event_handler_instance_t _evt_snsr_handler = nullptr; + esp_event_handler_instance_t _evt_ntfy_handler = nullptr; + esp_event_handler_instance_t _evt_set_handler = nullptr; + esp_event_handler_instance_t _evt_state_handler = nullptr; + + // event dispatcher + static void _event_picker(void* arg, esp_event_base_t base, int32_t id, void* event_data); + + // button events picker + void _evt_button(ESPButton::event_t e, const EventMsg* m) override; + // encoder events picker + void _evt_encoder(ESPButton::event_t e, const EventMsg* m) override; // sensor events handler - void _evt_sensor(int32_t id, void* data) override; + void _evt_sensor(int32_t id, void* data); // notify events handler - void _evt_notify(int32_t id, void* data) override; + void _evt_notify(int32_t id, void* data); // command events handler - void _evt_cmd(int32_t id, void* data) override; + void _evt_cmd(int32_t id, void* data); // state events handler - void _evt_state(int32_t id, void* data) override; + void _evt_state(int32_t id, void* data); public: - ViSet_MainScreen(); + ViSet_MainScreen(GPIOButton &button, PseudoRotaryEncoder &encoder); + + ~ViSet_MainScreen(); + + // renders Main working screen + void drawScreen() override; }; + /** - * @brief this class will draw and navigate through configuration menu + * @brief generic class for menu objects + * it will handle button and encoder events, screen refresh, etc... + * menu functions must be implemented in derived classes * */ -class ViSet_ConfigurationMenu : public VisualSet, public MuiPlusPlus { +class MuiMenu : public VisualSet, public MuiPlusPlus { - void _build_menu(); +protected: + // screen refresh required + bool _rr{true}; - void _build_menu_temp_opts(muiItemId parent, muiItemId header, muiItemId footer); + // button events picker + void _evt_button(ESPButton::event_t e, const EventMsg* m) override; + // encoder events picker + void _evt_encoder(ESPButton::event_t e, const EventMsg* m) override; +public: + // c-tor + MuiMenu(GPIOButton &button, PseudoRotaryEncoder &encoder); + + /** + * @brief handle that is called by screent refresh timer to redraw screen + * + */ + void drawScreen() override; +}; + + +/** + * @brief this class will draw and navigate through a + * different sections of configuration menu + * + */ +class ViSet_MainMenu : public MuiMenu { + + // menu builder function + void _buildMenu(); + + /** + * @brief a callback method that hooked to MuiItem_U8g2_DynamicScrollList + * it will pick index of a list item that represents desired submenu section + * and send event to HID class to switch ViSet instance to another menu object + * + */ + void _submenu_selector(size_t index); public: - ViSet_ConfigurationMenu(); + // c-tor + ViSet_MainMenu(GPIOButton &button, PseudoRotaryEncoder &encoder) : MuiMenu(button, encoder) { _buildMenu(); } - // MuiPlusPlus event sink, might be used in derrived classes - mui_event muipp_event(mui_event e) override; +}; - void drawScreen() override; + +/** + * @brief temperature control menu + * + */ +class ViSet_TemperatureSetup : public MuiMenu { + // menu builder function + void _buildMenu(); + +public: + // c-tor + ViSet_TemperatureSetup(GPIOButton &button, PseudoRotaryEncoder &encoder) : MuiMenu(button, encoder) { _buildMenu(); } }; diff --git a/SolderingPen_ESP32S2/lang/i18n.h b/SolderingPen_ESP32S2/lang/i18n.h index ea52877..8d7752b 100644 --- a/SolderingPen_ESP32S2/lang/i18n.h +++ b/SolderingPen_ESP32S2/lang/i18n.h @@ -14,7 +14,7 @@ #define MENU_MAIN_CFG_SIZE 6 -#define MENU_TEMPERATURE_CFG_SIZE 4 +#define MENU_TEMPERATURE_CFG_SIZE 5 /* // List of available translations enum lang_index : uint32_t { diff --git a/SolderingPen_ESP32S2/lang/lang_en_us.h b/SolderingPen_ESP32S2/lang/lang_en_us.h index f6ff89f..adde608 100644 --- a/SolderingPen_ESP32S2/lang/lang_en_us.h +++ b/SolderingPen_ESP32S2/lang/lang_en_us.h @@ -19,7 +19,7 @@ // 10x14 OK! #define MAIN_MENU_FONT2 u8g2_font_smart_patrol_nbp_tr // 10x14 OK! -#define MAIN_MENU_FONT3 u8g2_font_smart_patrol_nbp_tr +#define MAIN_MENU_FONT3 u8g2_font_unifont_t_cyrillic // 12x17 //#define MAIN_MENU_FONT3 u8g2_font_shylock_nbp_t_all @@ -102,5 +102,6 @@ static constexpr std::array menu_Temper lang_en_us::T_Temp_SaveLastWrk, lang_en_us::T_TempDefltWrk, lang_en_us::T_TempSleep, - lang_en_us::T_TempBoost + lang_en_us::T_TempBoost, + lang_en_us::T_return }; diff --git a/SolderingPen_ESP32S2/main.cpp b/SolderingPen_ESP32S2/main.cpp index b5efcf2..890a457 100644 --- a/SolderingPen_ESP32S2/main.cpp +++ b/SolderingPen_ESP32S2/main.cpp @@ -153,7 +153,7 @@ void setup() { // initialize HID (buttons controls and navigation, screen) LOGI(T_IRON, println, "Init HID"); - hid.init(espIron.getTemperatures()); + hid.init(); // long beep for setup completion 安装完成时长哔哔声 beep(); From 79d38b2a0fe8fea9fe71ecaa4aa558705e6e3839 Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Mon, 13 May 2024 18:07:23 +0900 Subject: [PATCH 6/6] Implement checkbox in temp config to enable/disable saving last work temperature Setting -> Temperature -> use last temp Checkbox controls if last used work temperature should be saved in NVS and restored on power on. If disabled (default) then work temperature from settings will be used on power on. If enabled, then each time work temp is changed, it will be saved/restored later ref issue: Eddddddddy/Songguo-PTS200#19 --- .gitignore | 1 + SolderingPen_ESP32S2/common.hpp | 3 ++- SolderingPen_ESP32S2/const.h | 8 +++--- SolderingPen_ESP32S2/evtloop.hpp | 2 ++ SolderingPen_ESP32S2/hid.cpp | 35 ++++++++++++++++++++----- SolderingPen_ESP32S2/hid.hpp | 10 ++++++- SolderingPen_ESP32S2/ironcontroller.cpp | 18 ++++++++++++- SolderingPen_ESP32S2/lang/i18n.h | 4 ++- SolderingPen_ESP32S2/lang/lang_en_us.h | 18 ++++++++----- SolderingPen_ESP32S2/nvs.cpp | 8 +++--- 10 files changed, 82 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 895fb10..2959175 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .DS_Store .idea .lnk +temp/ platformio_*.ini SolderingPen_ESP32S2/build SolderingPen_ESP32S2/.idea diff --git a/SolderingPen_ESP32S2/common.hpp b/SolderingPen_ESP32S2/common.hpp index e3b28d2..7b5949a 100644 --- a/SolderingPen_ESP32S2/common.hpp +++ b/SolderingPen_ESP32S2/common.hpp @@ -31,6 +31,7 @@ enum class ironState_t { // working temperature values struct Temperatures { - int32_t working{TEMP_DEFAULT}, standby{TEMP_SLEEP}, boost{TEMP_BOOST}; + int32_t working{TEMP_DEFAULT}, standby{TEMP_SLEEP}, boost{TEMP_BOOST}, deflt{TEMP_DEFAULT}; + bool savewrk{false}; }; diff --git a/SolderingPen_ESP32S2/const.h b/SolderingPen_ESP32S2/const.h index c8dbd83..45cb384 100644 --- a/SolderingPen_ESP32S2/const.h +++ b/SolderingPen_ESP32S2/const.h @@ -17,8 +17,8 @@ static constexpr const char* T_UI = "UI"; static constexpr const char* T_HID = "HID"; // NVS keys -static constexpr const char* T_timeouts = "timeouts"; -static constexpr const char* T_temperatures = "temperatures"; +static constexpr const char* T_timeouts = "timeouts"; // blob with timeout values +static constexpr const char* T_temperatures = "temperatures"; // blob with temperature values + +static constexpr const char* T_motionThr = "motionThr"; // motion threshold (uint32) -static constexpr const char* T_motionThr = "motionThr"; // motion threshold (uint32) -static constexpr const char* T_lang = "lang"; // UI language diff --git a/SolderingPen_ESP32S2/evtloop.hpp b/SolderingPen_ESP32S2/evtloop.hpp index cb401a3..cfb6af0 100644 --- a/SolderingPen_ESP32S2/evtloop.hpp +++ b/SolderingPen_ESP32S2/evtloop.hpp @@ -53,6 +53,8 @@ enum class iron_t:int32_t { workModeToggle, // toggle working mode on/off boostModeToggle, // toggle boost mode on/off + reloadTemp, // reload temperature configuration + // State notifications stateWorking = 300, stateStandby, // iron controller switched to 'Standby' mode diff --git a/SolderingPen_ESP32S2/hid.cpp b/SolderingPen_ESP32S2/hid.cpp index 37f6829..c6ce5ec 100644 --- a/SolderingPen_ESP32S2/hid.cpp +++ b/SolderingPen_ESP32S2/hid.cpp @@ -124,6 +124,7 @@ void IronHID::switchViSet(viset_evt_t v){ viset = std::make_unique(_btn, _encdr); break; }; + LOGI(T_HID, printf, "heap:%u\n", ESP.getFreeHeap()/1024); } void IronHID::init(){ @@ -236,14 +237,15 @@ ViSet_MainScreen::ViSet_MainScreen(GPIOButton &button, PseudoRot } ViSet_MainScreen::~ViSet_MainScreen(){ - esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_VISET, ESP_EVENT_ANY_ID, _evt_snsr_handler); + esp_event_handler_instance_unregister_with(evt::get_hndlr(), SENSOR_DATA, ESP_EVENT_ANY_ID, _evt_snsr_handler); _evt_snsr_handler = nullptr; - esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_VISET, ESP_EVENT_ANY_ID, _evt_ntfy_handler); + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_NOTIFY, ESP_EVENT_ANY_ID, _evt_ntfy_handler); _evt_ntfy_handler = nullptr; - esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_VISET, ESP_EVENT_ANY_ID, _evt_set_handler); + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_SET_EVT, ESP_EVENT_ANY_ID, _evt_set_handler); _evt_set_handler = nullptr; - esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_VISET, ESP_EVENT_ANY_ID, _evt_state_handler); + esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_STATE, ESP_EVENT_ANY_ID, _evt_state_handler); _evt_state_handler = nullptr; + LOG(println, "d-tor ViSet_MainScreen"); } void ViSet_MainScreen::drawScreen(){ @@ -577,7 +579,7 @@ void ViSet_MainMenu::_buildMenu(){ [this](size_t idx){ _submenu_selector(idx); }, // action callback MAIN_MENU_Y_SHIFT, 3, // offset for each line of text and total number of lines in menu MAIN_MENU_X_OFFSET, MAIN_MENU_Y_OFFSET, // x,y cursor - MAIN_MENU_FONT2, NULL + MAIN_MENU_FONT2, MAIN_MENU_FONT2 ); // set dynamic list will act as page selector @@ -605,6 +607,21 @@ void ViSet_MainMenu::_submenu_selector(size_t index){ // ************************************** // *** Temperature Control Menu *** +ViSet_TemperatureSetup::ViSet_TemperatureSetup(GPIOButton &button, PseudoRotaryEncoder &encoder) : MuiMenu(button, encoder){ + // load temperature values from NVS + nvs_blob_read(T_IRON, T_temperatures, static_cast(&_temp), sizeof(Temperatures)); + + _buildMenu(); +} + +ViSet_TemperatureSetup::~ViSet_TemperatureSetup(){ + //LOGD(T_HID, println, "save temp settings"); + // save temp settings to NVS + nvs_blob_write(T_IRON, T_temperatures, static_cast(&_temp), sizeof(Temperatures)); + // send command to reload temp settings + EVT_POST(IRON_SET_EVT, e2int(iron_t::reloadTemp)); +} + void ViSet_TemperatureSetup::_buildMenu(){ // create page "Settings->Temperature" muiItemId root_page = makePage(menu_MainConfiguration.at(0)); @@ -643,10 +660,14 @@ void ViSet_TemperatureSetup::_buildMenu(){ // page title element addItemToPage(page_title_id, page_savewrkT); - // checkbox - MuiItem_U8g2_CheckBox *box = new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), "Some box with very long text here...", true, nullptr, MAINSCREEN_FONT, 0, 25); + // save last work temp checkbox + MuiItem_U8g2_CheckBox *box = new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), dictionary[D_SaveLast_box], _temp.savewrk, [this](size_t v){ _temp.savewrk = v; }, MAINSCREEN_FONT, 0, 25); addMuippItem(box, page_savewrkT); + // text hint + MuiItem_U8g2_StaticText *txt = new MuiItem_U8g2_StaticText(u8g2, nextIndex(), dictionary[D_SaveLast_hint], MAIN_MENU_FONT1, 0, 25); + addMuippItem(txt, page_savewrkT); + // generate idx for "Back Button" item muiItemId bbuton_id = nextIndex(); // create "Back Button" item diff --git a/SolderingPen_ESP32S2/hid.hpp b/SolderingPen_ESP32S2/hid.hpp index 420c07a..ca21b59 100644 --- a/SolderingPen_ESP32S2/hid.hpp +++ b/SolderingPen_ESP32S2/hid.hpp @@ -243,12 +243,20 @@ class ViSet_MainMenu : public MuiMenu { * */ class ViSet_TemperatureSetup : public MuiMenu { + // configured temperatures + Temperatures _temp; + + // callback for "Save work temp" option + //void _cb_save_last_temp(size_t v); + // menu builder function void _buildMenu(); public: // c-tor - ViSet_TemperatureSetup(GPIOButton &button, PseudoRotaryEncoder &encoder) : MuiMenu(button, encoder) { _buildMenu(); } + ViSet_TemperatureSetup(GPIOButton &button, PseudoRotaryEncoder &encoder); + // d-tor + ~ViSet_TemperatureSetup(); }; diff --git a/SolderingPen_ESP32S2/ironcontroller.cpp b/SolderingPen_ESP32S2/ironcontroller.cpp index dc232e8..17572d0 100644 --- a/SolderingPen_ESP32S2/ironcontroller.cpp +++ b/SolderingPen_ESP32S2/ironcontroller.cpp @@ -44,6 +44,10 @@ void IronController::init(){ // load temperature values from NVS nvs_blob_read(T_IRON, T_temperatures, static_cast(&_temp), sizeof(Temperatures)); + // if we are not saving working temp, then use default one instead + if (!_temp.savewrk) + _temp.working = _temp.deflt; + // start mode switcher timer if (!_tmr_mode){ _tmr_mode = xTimerCreate("modeT", @@ -218,13 +222,25 @@ void IronController::_evt_commands(esp_event_base_t base, int32_t id, void* data int32_t t = *reinterpret_cast(data); if (t != _temp.working){ _temp.working = *reinterpret_cast(data); - nvs_blob_write(T_IRON, T_temperatures, static_cast(&_temp), sizeof(Temperatures)); + // save new working temp to NVS only if respective flag is set + if (_temp.savewrk) + nvs_blob_write(T_IRON, T_temperatures, static_cast(&_temp), sizeof(Temperatures)); } if (_state == ironState_t::working) EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::heaterTargetT), &_temp.working, sizeof(_temp.working)); break; } + // reload temp configuration + case evt::iron_t::reloadTemp : { + LOGV(T_HID, println, "reload temp settings"); + // load temperature values from NVS + nvs_blob_read(T_IRON, T_temperatures, static_cast(&_temp), sizeof(Temperatures)); + + // if we are not saving working temp, then use default one instead + if (!_temp.savewrk) + _temp.working = _temp.deflt; + } // some } diff --git a/SolderingPen_ESP32S2/lang/i18n.h b/SolderingPen_ESP32S2/lang/i18n.h index 8d7752b..2aa9eab 100644 --- a/SolderingPen_ESP32S2/lang/i18n.h +++ b/SolderingPen_ESP32S2/lang/i18n.h @@ -37,9 +37,11 @@ enum dict_index { D_idle, D_notip, D_return, + D_SaveLast_box, + D_SaveLast_hint, D_Settings, D_set_t, D_standby, D_Temp_SaveLastWrkDescr, - D_____SIZE + DICT___SIZE }; diff --git a/SolderingPen_ESP32S2/lang/lang_en_us.h b/SolderingPen_ESP32S2/lang/lang_en_us.h index adde608..53893b9 100644 --- a/SolderingPen_ESP32S2/lang/lang_en_us.h +++ b/SolderingPen_ESP32S2/lang/lang_en_us.h @@ -61,10 +61,14 @@ static constexpr const char* T_Information = "Information"; static constexpr const char* T_Language = "Language"; // Temperature settings menu -static constexpr const char* T_Temp_SaveLastWrk = "Save work T"; -static constexpr const char* T_TempDefltWrk = "Default work T"; -static constexpr const char* T_TempSleep = "Standby temp"; -static constexpr const char* T_TempBoost = "Boost increase T"; +static constexpr const char* T_SaveLastT = "Save work temp."; +static constexpr const char* T_TempDefltWrk = "Default temp."; +static constexpr const char* T_TempSleep = "Standby temp."; +static constexpr const char* T_TempBoost = "Boost step T"; +// Temperature settings hints +static constexpr const char* T_SaveLast_box = "use last temp."; +static constexpr const char* T_SaveLast_hint = "instead of default one"; + } // end of namespace lang_en_us @@ -74,13 +78,15 @@ static constexpr const char* T_TempBoost = "Boost increase T"; * order of items MUST match enum dict_index * */ -static constexpr std::array dictionary = { +static constexpr std::array dictionary = { lang_en_us::T_Boost, lang_en_us::T_Error, lang_en_us::T_Heating, lang_en_us::T_Idle, lang_en_us::T_NoTip, lang_en_us::T_return, + lang_en_us::T_SaveLast_box, + lang_en_us::T_SaveLast_hint, lang_en_us::T_Settings, lang_en_us::T_setT, lang_en_us::T_standby, @@ -99,7 +105,7 @@ static constexpr std::array menu_MainConfigura // Main Configuration menu items static constexpr std::array menu_TemperatureOpts = { - lang_en_us::T_Temp_SaveLastWrk, + lang_en_us::T_SaveLastT, lang_en_us::T_TempDefltWrk, lang_en_us::T_TempSleep, lang_en_us::T_TempBoost, diff --git a/SolderingPen_ESP32S2/nvs.cpp b/SolderingPen_ESP32S2/nvs.cpp index f85140e..337417a 100644 --- a/SolderingPen_ESP32S2/nvs.cpp +++ b/SolderingPen_ESP32S2/nvs.cpp @@ -21,13 +21,13 @@ esp_err_t nvs_blob_read(const char* nvsspace, const char* key, void* blob, size_ std::unique_ptr nvs = nvs::open_nvs_handle(nvsspace, NVS_READONLY, &err); if (err != ESP_OK) { - LOGD(T_NVS, printf, "Err opening NVS namespace:%s RO, err:%s\n", nvsspace, esp_err_to_name(err)); + LOGD(T_NVS, printf, "Err opening NVS ns:%s RO, err:%s\n", nvsspace, esp_err_to_name(err)); return err; } err = nvs->get_blob(key, blob, len); if (err != ESP_OK) { - LOGD(T_NVS, printf, "Err reading NVS blob namespace:%s, key:%s, err:%s\n", nvsspace, key, esp_err_to_name(err)); + LOGD(T_NVS, printf, "Err reading NVS blob ns:%s, key:%s, err:%s\n", nvsspace, key, esp_err_to_name(err)); } return err; } @@ -37,13 +37,13 @@ esp_err_t nvs_blob_write(const char* nvsspace, const char* key, void* blob, size std::unique_ptr nvs = nvs::open_nvs_handle(nvsspace, NVS_READWRITE, &err); if (err != ESP_OK) { - LOGD(T_NVS, printf, "Err opening NVS namespace:%s RW, err:%s\n", nvsspace, esp_err_to_name(err)); + LOGD(T_NVS, printf, "Err opening NVS ns:%s RW, err:%s\n", nvsspace, esp_err_to_name(err)); return err; } err = nvs->set_blob(key, blob, len); if (err != ESP_OK) { - LOGD(T_NVS, printf, "Err writing NVS namespace:%s, key:%s, err:%s\n", nvsspace, key, esp_err_to_name(err)); + LOGD(T_NVS, printf, "Err writing NVS ns:%s, key:%s, err:%s\n", nvsspace, key, esp_err_to_name(err)); } return err; }