Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made changes to enable high power output #5

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
60 changes: 55 additions & 5 deletions src/heating.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ class Heating {
static const int PID_K_PROPORTIONAL = 450;
static const int PID_K_INTEGRAL = 1000;
static const int PID_K_DERIVATE = 50;
static const int HEATING_POWER_MAX_MW = 40 * 1000; // mW
static const int RTM_HEATING_POWER_MAX_MW = 40 * 1000; // mW
static const int RTU_HEATING_POWER_MAX_MW = 150 * 1000; // mW
static const int IDLE_MIN_TIME_MS = 3; // ms
static const int STABILIZE_TIME_MS = 2; // ms
static const int HEATING_MIN_POWER_MW = 100; // mW
static const int TIP_MAX_CURRENT_MA = 9000; // mA
static const int SUPPLY_VOLTAGE_HEATING_MIN_MV = 4300; // mV
static const int SUPPLY_VOLTAGE_MAX_MV = 18000; // mV
static const int TIP_RESISTANCE_SHORTED_MO = 500; // mOhm
static const int RTM_TIP_RESISTANCE_MIN_MO = 1500 // mOhm
static const int RTM_TIP_RESISTANCE_MAX_MO = 2500 // mOhm
static const int RTU_TIP_RESISTANCE_MAX_MO = 4000 // mOhm
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RTU tip has normally 3.5ohm, it is OK if upper limit is 4ohm, but i suggest to set also MIN for RTU, to warn, or make fallback switch to RTM if there will be something less than 2.5ohm

in general it is possible to automatically select the right tip with resistance measurement

what is your opinion?
because if you select high power and then you insert RTM then you immediately broke it.

so option one: write message that is 'shorted' or fallback to RTM power limit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, there is a max resistance for the RTM tips. So the logic checks whether it is between min and max of the RTM (1.5 - 2.5 ohm). Then it is RTM. If it is below 1.5 ohm then it is "low resistance" and essentially an error state of some sort. If it is over 2.5 ohm, we can assume it is RTU, so it sets RTU, UNLESS it is higher than the RTU max (4 ohm) in which case we call it "high resistance" which I understand to also be an error state.

If we added an RTU minimum, then the only new functionality it would allow is to detect an error state in between the two types of tips. So, do we think it is likely that we will see a resistance in between 2.5 and 3 ohm and want to declare that an error state? Our ranges for resistance are arbitrary (RTM is about 2ohm, so we capture anything from 1.5 to 2.5, and then I did the same for RTU, from 3 to 4). But if an RTU tip read 2.9 ohms, would we want it to declare an error, or just run normally?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for selecting high power when a RTM tip is inserted, my understanding is that resistance is detected on every clock cycle (that's how I am reading the code, every "tick" is a cycle, is this correct?) that PWM is off, and max power is adjusted the very next clock cycle. So we would very quickly detect that it was an RTM tip, and adjust power limit accordingly, and my assumption is that this wouldn't cause much, if any, damage.

I also set the code so that when the tip is "unknown" (when we first power up the iron, before it runs, it cannot detect resistance), the power limit is 150mW, which isn't even high enough to get the iron warm. But it's just enough power that the iron can detect resistance, and then it adjusts power level. This way, like you said, it won't put full power to a RTM tip (or a tip where it can't tell what it is). Perhaps I can think of a way to always do that on the first cycle when you start heating. That way no matter what, max power will be 150mW until the iron knows for sure what tip is installed.

In my mind there are two ways to do this. We could do it at the beginning or the end. Either very time the "start" routine is called, the iron is set to TipType "UNKNOWN" so that it is forced to 150mW until resistance is confirmed. Or any time the "stop" routine is called, we set TipType to UNKNOWN. Then the routine will immediately determine tip type and continue to function. Then when the user stops the iron, it will remember what type of tip is connected until they start heating again. This could be useful if we, for example, replace the voltage drop display (I don't personally find this useful, maybe it's good for debugging?) with the tip type instead, so a user knows what is detected on their iron (in case they were confused or forgot which tip was which). But once you start you will never accidentally feed 150W to a 40W tip, even for a microsecond.

I will see if I can add this to the code, I don't think it will be too difficult, should just be one line of code, in state_start() or a similar routine, maybe in main.hpp. I will look into it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RTU can have between 3 and 4ohm, if it will be less (e.g 2.9ohm), then it is a problem, because at 24V it will have peaks higher than 200Watts
If someone find RTU tips with less than 3ohm, we can discuss this again, but now I think it is clear

about detecting resistance: YES I checking resistance in very fast loop when is PWM in ON state, then I immediately react on low resistance or too high power and PWM is stopped immediately in microseconds - reason why PWM is driven by SW and not HW PWM peripheral.

Later I try to draw image where I explain heating principle, but be sure, that detecting resistance is possible in microseconds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I assumed it happened super fast since it was basically every clock cycle. I highly doubt that applying 150W to a 40W tip for that short a period of time (if it even achieved that high power that quickly) would cause any serious damage.

But last night I did add a line to the _state_start() routine where all the variables are reset to zero that also sets _tip_type to UNKNOWN. So basically every time it starts the heating cycle it redetects the tip type and until it does, the max power is 150mW. I'm not sure if running that routine constantly might have an effect on the pid loop (since I think that means it will constantly be set to a max of 150mW and then back to 40W or 150W?). I ran my 40W tip yesterday to test it, and it seemed like the temperature settled one or two degrees low. I can't remember if it did that before, but I don't think so, if anything it was a couple degrees high before.

As for the resistance, you make a good point. I will establish a minimum as well so that a resistance below 3ohm but above 2.5 will register as an error state and neither RTU or RTP. That is an easy change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so option one: write message that is 'shorted' or fallback to RTM power limit.

I didn't see this before. Yeah, I suppose instead of being set to RTU when the resistance is over 2.5, it could be set as RTU when resistance is over 3. This would expand the range of RTM though and might miss an RTM tip that was failing and had a high resistance (does that happen when they fail? I assume it does). I don't know how quickly the resistance increases but if we have the error state in between we might catch a failing RTM instead of putting 150W into it and very quickly destroying it, maybe causing more damage to something else.

So I think you were right the first time, have a gap between the two types of tips that is accurate to how they are really built.

Copy link
Contributor Author

@swissfreek swissfreek Jan 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also in the check_tip_type(), this will allow a tip type of "UNKNOWN" if the resistance is in between the two, because this is true, we don't know if it's an RTU tip with low resistance, or an RTM tip with high resistance. And since it is declared as UNKNOWN in this case, the max power output will be 150mW which is very safe, even if the rest of the code allows the heater to receive power anyway.

static const int TIP_RESISTANCE_MIN_MO = 1500; // mOhm
static const int TIP_RESISTANCE_MAX_MO = 2500; // mOhm
static const int TIP_RESISTANCE_BROKEN_MO = 100000; // mOhm
Expand Down Expand Up @@ -54,6 +58,13 @@ class Heating {
BROKEN,
SHORTED,
} _tip_sensor_status = TipSensorStatus::UNKNOWN;

/* defines new class for tip type and sets default to UNKNOWN */
enum class TipType {
UNKNOWN,
RTM,
RTU,
} _tip_type = TipType::UNKNOWN

private:
Settings &_settings;
Expand All @@ -72,6 +83,7 @@ class Heating {

int _requested_power_mw = 0; // mW
int _power_mw = 0; // mW
// int _max_power_mw = 0;
int _cpu_voltage_mv_heat = 0; // mV
int _cpu_voltage_mv_idle = 0; // mV
int _supply_voltage_mv_heat = 0; // mV
Expand Down Expand Up @@ -207,17 +219,28 @@ class Heating {
void _check_heating_element() {
if (_heater_resistance_mo < TIP_RESISTANCE_SHORTED_MO) {
_heating_element_status = HeatingElementStatus::SHORTED;
} else if (_heater_resistance_mo < TIP_RESISTANCE_MIN_MO) {
} else if (_heater_resistance_mo < RTM_TIP_RESISTANCE_MIN_MO) {
_heating_element_status = HeatingElementStatus::LOW_RESISTANCE;
} else if (_heater_resistance_mo > TIP_RESISTANCE_BROKEN_MO) {
_heating_element_status = HeatingElementStatus::BROKEN;
} else if (_heater_resistance_mo > TIP_RESISTANCE_MAX_MO) {
} else if (_heater_resistance_mo > RTU_TIP_RESISTANCE_MAX_MO) {
_heating_element_status = HeatingElementStatus::HIGH_RESISTANCE;
} else {
_heating_element_status = HeatingElementStatus::OK;
}
}


/* Checks if resistance is in range of RTM or above it (RTU) or other (unknown) */
void _check_tip_type() {
if (_heater_resistance_mo > RTM_TIP_RESISTANCE_MAX_MO && _heater_resistance_mo < RTU_TIP_RESISTANCE_MAX_MO) {
_tip_type = TipType::RTU;
} else if (_heater_resistance_mo > RTM_TIP_RESISTANCE_MIN_MO && _heater_resistance_mo < RTM_TIP_RESISTACNCE_MAX_MO) {
_tip_type = TipType::RTM;
} else {
_tip_type = TipType::UNKNOWN;
}
}

void _state_heating(const unsigned delta_ticks) {
_measure_ticks += delta_ticks;
if (board::Adc::get_instance().process() != board::Adc::State::DONE) return;
Expand All @@ -231,6 +254,7 @@ class Heating {
_average_heating_measured_values();
_calculate_total_energy();
_calculate_tip_resistance();
_check_tip_type(); // establishes tip type once resistance is calculated
_calculate_voltage_drop();
_check_heating_element();
_state = State::STABILIZE;
Expand Down Expand Up @@ -461,11 +485,21 @@ class Heating {
TipSensorStatus getTipSensorStatus() const {
return _tip_sensor_status;
}

/** Getter tip type
indicate whether RTM or RTU tip is detected

Return:
state from enum TipType
*/
TipType getTipType() const {
return _tip_type;
}

/** Initialize module
*/
void init() {
_pid.set_constants(PID_K_PROPORTIONAL, PID_K_INTEGRAL, PID_K_DERIVATE, PERIOD_TIME_MS, HEATING_POWER_MAX_MW);
// _pid.set_constants(PID_K_PROPORTIONAL, PID_K_INTEGRAL, PID_K_DERIVATE, PERIOD_TIME_MS, HEATING_POWER_MAX_MW);
}

Preset &get_preset() {
Expand All @@ -475,6 +509,22 @@ class Heating {
/** Start heating cycle
*/
void start() {
// _max_power_mw = (40 + (110 * _settings.get_high_power())) * 100; //mW
// _pid.set_constants(PID_K_PROPORTIONAL, PID_K_INTEGRAL, PID_K_DERIVATE, PERIOD_TIME_MS, _max_power_mw);
if (_tip_type == TipType::RTU) {
_pid.set_constants(PID_K_PROPORTIONAL, PID_K_INTEGRAL, PID_K_DERIVATE, PERIOD_TIME_MS, RTU_HEATING_POWER_MAX_MW);
if (!_settings.get_high_power()) {
_settings.toggle_high_power();
_settings.save();
}
}
else {
_pid.set_constants(PID_K_PROPORTIONAL, PID_K_INTEGRAL, PID_K_DERIVATE, PERIOD_TIME_MS, RTM_HEATING_POWER_MAX_MW);
if (_settings.get_high_power()) {
_settings.toggle_high_power();
_settings.save();
}
}
if (_preset.is_standby() || getTipSensorStatus() != Heating::TipSensorStatus::OK) {
_pid.reset();
_requested_power_mw = 0;
Expand Down
7 changes: 6 additions & 1 deletion src/screen/main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,12 @@ class Main : public Screen {
if (_preset.is_standby()) return;

int power = _heating.get_power_mw();
int len = power * 15 / 40000;
int len = power * 15 / 40000; // default for 40W tips

// account for 150W tips
if (_heating.getTipType() == Heating::TipType::RTU) {
len = power * 15 / 150000;
}

if (len == 0 && power > 0) len = 1;
if (len > 15) len = 15;
Expand Down
8 changes: 8 additions & 0 deletions src/screen/menu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Menu : public Screen {
ITEM_ADVANCED_MODE,
ITEM_FAHRENHEIT,
ITEM_LEFT_HANDED,
ITEM_HIGH_POWER,
ITEM_TOTAL_ENERGY,
ITEM_HEATER_POWER,
ITEM_HEATER_CURRENT,
Expand Down Expand Up @@ -88,6 +89,13 @@ class Menu : public Screen {
0,
MenuItem::ItemType::VALUE_BINARY,
true,
}, {
"High power:";
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comma

Copy link
Contributor Author

@swissfreek swissfreek Jan 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh, good catch, thank you. But honestly, if we agree that it is better to automatically detect power levels (which I think also, I just hadn't figured out how so I was forcing it with a menu), then all these code changes would go away. The only change required would be to heating.hpp and screen/main.hpp (for the power bar). Settings.hpp and screen/menu.hpp could be unchanged from your version.

But, if the intent is to add RTPico tip function, then we cannot auto-detect this, as they have the same resistance as RTM (I think that's correct?). So you would have to have a setting for RTM vs. RTP to change the PIDs used, if new PIDs are developed for all three tips.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, detecting RTM vs RTU is easy, but RTM vs RTP is also possible, RTP tips are very small and my idea is to somehow provide "heating speed" parameter from PID regulation loop, this can easily identify TIP type. but currently it is only idea, I have no practical researches about this ,-)

Another idea is to select right tip from menu and all these mechanisms can only stop heating if some detection will say something else.

Also RTM and RTP tips uses same parameters (40W/12V/2ohm) so there is no problem.. ok one, RTP tips require little different PID constants.

Copy link
Contributor Author

@swissfreek swissfreek Jan 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would much much prefer to be able to switch between RTU and RTM and not have to worry about going into menus to make changes. But I also don't intend to use RTP tips very much, if at all, it's just not the type of soldering I do. But, I think, if there will be different PIDs for RTP and RTM, then that could be a boolean setting in the menu. I suspect it's pretty unlikely that someone would be switching constantly between RTU, RTM, and RTP all at the same time. Probably more likely to use RTU/RTM together as needed, and then RTM/RTP together as needed. In my mind, RTM is still the primary tip, and RTU or RTP are for situations where the RTM doesn't have the right features. So at least if one of the parts can be automatic, that would probably make life easier for some folks.

And then if you figure out a way to automate detection of the RTP/RTM, then that can be added, too. But that stuff is way above my skill level, haha.

mullptr,
0,
0,
MenuItem::ItemType::VALUE_BINARY,
true,
}, {
"Total energy:",
" Wh",
Expand Down
19 changes: 18 additions & 1 deletion src/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Settings {
PRESET_TEMPERATURE1,
PRESET_TEMPERATURE2,
STANDBY_TIMEOUT,
HIGH_POWER,
};

lib::Nv nv;
Expand All @@ -28,8 +29,9 @@ class Settings {
bool _fahrenheit;
bool _left_handed;
bool _brightness;
bool _high_power;

static const size_t MIN_FREE = 7;
static const size_t MIN_FREE = 8;

public:

Expand All @@ -41,6 +43,7 @@ class Settings {
_preset_temperatures[0] = nv.load_u16(PRESET_TEMPERATURE1, Default::PRESET_TEMPERATURE1);
_preset_temperatures[1] = nv.load_u16(PRESET_TEMPERATURE2, Default::PRESET_TEMPERATURE2);
_standby_timeout = nv.load_u16(STANDBY_TIMEOUT, Default::STANDBY_TIMEOUT);
_high_power = nv.load_bool(HIGH_POWER, false);
}

void save() {
Expand All @@ -51,6 +54,7 @@ class Settings {
nv.save_bool(BRIGHTNESS, _brightness);
nv.save_u16(PRESET_TEMPERATURE1, _preset_temperatures[0]);
nv.save_u16(PRESET_TEMPERATURE2, _preset_temperatures[1]);
nv.save_bool(HIGH_POWER, _high_power);
}


Expand Down Expand Up @@ -91,6 +95,19 @@ class Settings {
void set_left_handed(const bool val=true) {
_left_handed = val;
}


bool get_high_power() const {
return _high_power;
}

void toggle_high_power() {
_high_power = != _high_power;
}

void set_high_power() const {
_high_power = val;
}

static const uint8_t BRIGHTNESS_LOW = 40;
static const uint8_t BRIGHTNESS_HIGH = 240;
Expand Down