Commit | Line | Data |
---|---|---|
abe97b79 | 1 | /* |
2 | This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). | |
3 | Smoothie 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. | |
4 | Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |
5 | You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. | |
6 | */ | |
7 | ||
8 | /* | |
9 | TemperatureSwitch is an optional module that will automatically turn on or off a switch | |
10 | based on a setpoint temperature. It is commonly used to turn on/off a cooling fan or water pump | |
11 | to cool the hot end's cold zone. Specifically, it turns one of the small MOSFETs on or off. | |
12 | ||
13 | Author: Michael Hackney, mhackney@eclecticangler.com | |
14 | */ | |
15 | ||
16 | #include "TemperatureSwitch.h" | |
17 | #include "libs/Module.h" | |
18 | #include "libs/Kernel.h" | |
19 | #include "modules/tools/temperaturecontrol/TemperatureControlPublicAccess.h" | |
20 | #include "SwitchPublicAccess.h" | |
21 | ||
22 | #include "utils.h" | |
23 | #include "Gcode.h" | |
24 | #include "Config.h" | |
25 | #include "ConfigValue.h" | |
26 | #include "checksumm.h" | |
27 | #include "PublicData.h" | |
28 | #include "StreamOutputPool.h" | |
02e4b295 | 29 | #include "TemperatureControlPool.h" |
abe97b79 | 30 | |
1dc339ee | 31 | #define temperatureswitch_checksum CHECKSUM("temperatureswitch") |
5cbf2253 | 32 | #define enable_checksum CHECKSUM("enable") |
1dc339ee | 33 | #define temperatureswitch_hotend_checksum CHECKSUM("hotend") |
34 | #define temperatureswitch_threshold_temp_checksum CHECKSUM("threshold_temp") | |
35 | #define temperatureswitch_type_checksum CHECKSUM("type") | |
fe3323e7 | 36 | #define temperatureswitch_switch_checksum CHECKSUM("switch") |
1dc339ee | 37 | #define temperatureswitch_heatup_poll_checksum CHECKSUM("heatup_poll") |
38 | #define temperatureswitch_cooldown_poll_checksum CHECKSUM("cooldown_poll") | |
c0c375b0 JM |
39 | #define temperatureswitch_trigger_checksum CHECKSUM("trigger") |
40 | #define temperatureswitch_inverted_checksum CHECKSUM("inverted") | |
81300c80 | 41 | #define temperatureswitch_arm_command_checksum CHECKSUM("arm_mcode") |
5cbf2253 | 42 | #define designator_checksum CHECKSUM("designator") |
1dc339ee | 43 | |
abe97b79 | 44 | TemperatureSwitch::TemperatureSwitch() |
45 | { | |
5cbf2253 JM |
46 | this->temperatureswitch_state = false; |
47 | this->second_counter = 0; | |
abe97b79 | 48 | } |
49 | ||
50 | // Load module | |
51 | void TemperatureSwitch::on_module_loaded() | |
52 | { | |
5cbf2253 JM |
53 | vector<uint16_t> modulist; |
54 | // allow for multiple temperature switches | |
55 | THEKERNEL->config->get_module_list(&modulist, temperatureswitch_checksum); | |
56 | for (auto m : modulist) { | |
57 | load_config(m); | |
58 | } | |
e6b0fc38 | 59 | |
5cbf2253 JM |
60 | // no longer need this instance as it is just used to load the other instances |
61 | delete this; | |
62 | } | |
63 | ||
64 | ||
65 | bool TemperatureSwitch::load_config(uint16_t modcs) | |
66 | { | |
67 | // see if enabled | |
68 | if (!THEKERNEL->config->value(temperatureswitch_checksum, modcs, enable_checksum)->by_default(false)->as_bool()) { | |
69 | return false; | |
abe97b79 | 70 | } |
71 | ||
5cbf2253 JM |
72 | // create a temperature control and load settings |
73 | ||
74 | char designator= 0; | |
75 | string s= THEKERNEL->config->value(temperatureswitch_checksum, modcs, designator_checksum)->by_default("")->as_string(); | |
76 | if(s.empty()){ | |
604c4d0a | 77 | // for backward compatibility temperatureswitch.hotend will need designator 'T' by default @DEPRECATED |
5cbf2253 JM |
78 | if(modcs == temperatureswitch_hotend_checksum) designator= 'T'; |
79 | ||
80 | }else{ | |
81 | designator= s[0]; | |
82 | } | |
abe97b79 | 83 | |
5cbf2253 | 84 | if(designator == 0) return false; // no designator then not valid |
57235957 | 85 | |
5cbf2253 JM |
86 | // create a new temperature switch module |
87 | TemperatureSwitch *ts= new TemperatureSwitch(); | |
88 | ||
89 | // get the list of temperature controllers and remove any that don't have designator == specified designator | |
02e4b295 | 90 | auto& tempcontrollers= THEKERNEL->temperature_control_pool->get_controllers(); |
5cbf2253 JM |
91 | |
92 | // see what its designator is and add to list of it the one we specified | |
f53fe49d | 93 | void *returned_temp; |
5cbf2253 | 94 | for (auto controller : tempcontrollers) { |
f53fe49d | 95 | bool temp_ok = PublicData::get_value(temperature_control_checksum, controller, current_temperature_checksum, &returned_temp); |
96 | if (temp_ok) { | |
97 | struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_temp); | |
5cbf2253 JM |
98 | if (temp.designator[0] == designator) { |
99 | ts->temp_controllers.push_back(controller); | |
f53fe49d | 100 | } |
101 | } | |
102 | } | |
57235957 | 103 | |
5cbf2253 JM |
104 | // if we don't have any matching controllers, then not valid |
105 | if (ts->temp_controllers.empty()) { | |
106 | delete ts; | |
107 | return false; | |
57235957 JM |
108 | } |
109 | ||
abe97b79 | 110 | // load settings from config file |
fe3323e7 | 111 | s = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_switch_checksum)->by_default("")->as_string(); |
5cbf2253 | 112 | if(s.empty()) { |
fe3323e7 JM |
113 | // handle old configs where this was called type @DEPRECATED |
114 | s = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_type_checksum)->by_default("")->as_string(); | |
115 | if(s.empty()) { | |
116 | // no switch specified so invalid entry | |
117 | delete this; | |
118 | return false; | |
119 | } | |
5cbf2253 | 120 | } |
c0c375b0 JM |
121 | |
122 | // if we should turn the switch on or off when trigger is hit | |
123 | ts->inverted = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_inverted_checksum)->by_default(false)->as_bool(); | |
124 | ||
125 | // if we should trigger when above and below, or when rising through, or when falling through the specified temp | |
126 | string trig = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_trigger_checksum)->by_default("level")->as_string(); | |
81300c80 JM |
127 | if(trig == "level") ts->trigger= LEVEL; |
128 | else if(trig == "rising") ts->trigger= RISING; | |
129 | else if(trig == "falling") ts->trigger= FALLING; | |
130 | else ts->trigger= LEVEL; | |
131 | ||
132 | // the mcode used to arm the switch | |
133 | ts->arm_mcode = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_arm_command_checksum)->by_default(0)->as_number(); | |
134 | // if not defined then always armed, otherwise start out disarmed | |
135 | if(ts->arm_mcode == 0) ts->armed= true; | |
136 | else ts->armed= false; | |
c0c375b0 | 137 | |
fe3323e7 | 138 | ts->temperatureswitch_switch_cs= get_checksum(s); // checksum of the switch to use |
abe97b79 | 139 | |
5cbf2253 | 140 | ts->temperatureswitch_threshold_temp = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_threshold_temp_checksum)->by_default(50.0f)->as_number(); |
abe97b79 | 141 | |
5cbf2253 JM |
142 | // these are to tune the heatup and cooldown polling frequencies |
143 | ts->temperatureswitch_heatup_poll = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_heatup_poll_checksum)->by_default(15)->as_number(); | |
144 | ts->temperatureswitch_cooldown_poll = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_cooldown_poll_checksum)->by_default(60)->as_number(); | |
145 | ts->current_delay = ts->temperatureswitch_heatup_poll; | |
f53fe49d | 146 | |
c0c375b0 | 147 | // set initial state |
81300c80 JM |
148 | float current_temp = ts->get_highest_temperature(); |
149 | ts->lower= current_temp < ts->temperatureswitch_threshold_temp; | |
c0c375b0 | 150 | |
f53fe49d | 151 | // Register for events |
5cbf2253 | 152 | ts->register_for_event(ON_SECOND_TICK); |
81300c80 | 153 | ts->register_for_event(ON_GCODE_RECEIVED); |
5cbf2253 | 154 | return true; |
abe97b79 | 155 | } |
156 | ||
81300c80 JM |
157 | void TemperatureSwitch::on_gcode_received(void *argument) |
158 | { | |
159 | if(this->arm_mcode == 0) return; | |
160 | ||
161 | Gcode *gcode = static_cast<Gcode *>(argument); | |
162 | if(gcode->has_m && gcode->m == this->arm_mcode) { | |
163 | this->armed= (gcode->has_letter('S') && gcode->get_value('S') != 0); | |
164 | } | |
165 | } | |
166 | ||
abe97b79 | 167 | // Called once a second but we only need to service on the cooldown and heatup poll intervals |
168 | void TemperatureSwitch::on_second_tick(void *argument) | |
169 | { | |
170 | second_counter++; | |
171 | if (second_counter < current_delay) { | |
172 | return; | |
c0c375b0 | 173 | |
abe97b79 | 174 | } else { |
175 | second_counter = 0; | |
176 | float current_temp = this->get_highest_temperature(); | |
57235957 | 177 | |
abe97b79 | 178 | if (current_temp >= this->temperatureswitch_threshold_temp) { |
c0c375b0 JM |
179 | // temp >= threshold temp, call set_switch if trigger is LEVEL, or if we were lower and RISING |
180 | if (this->trigger == LEVEL || (this->lower && this->trigger == RISING)) { | |
181 | this->lower= false; | |
abe97b79 | 182 | set_switch(true); |
57235957 | 183 | current_delay = temperatureswitch_cooldown_poll; |
abe97b79 | 184 | } |
c0c375b0 JM |
185 | if(this->lower && this->trigger == FALLING) { |
186 | this->lower= false; | |
187 | } | |
188 | ||
abe97b79 | 189 | } else { |
c0c375b0 JM |
190 | // temp < threshold temp, call set_switch if trigger is LEVEL, or if we were not lower and FALLING |
191 | if (this->trigger == LEVEL || (!this->lower && this->trigger == FALLING)) { | |
192 | this->lower= true; | |
abe97b79 | 193 | set_switch(false); |
194 | current_delay = temperatureswitch_heatup_poll; | |
195 | } | |
c0c375b0 JM |
196 | if(!this->lower && this->trigger == RISING) { |
197 | this->lower= true; | |
198 | } | |
199 | } | |
abe97b79 | 200 | } |
201 | } | |
202 | ||
203 | // Get the highest temperature from the set of temperature controllers | |
204 | float TemperatureSwitch::get_highest_temperature() | |
205 | { | |
206 | void *returned_temp; | |
207 | float high_temp = 0.0; | |
208 | ||
209 | for (auto controller : temp_controllers) { | |
210 | bool temp_ok = PublicData::get_value(temperature_control_checksum, controller, current_temperature_checksum, &returned_temp); | |
211 | if (temp_ok) { | |
212 | struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_temp); | |
213 | // check if this controller's temp is the highest and save it if so | |
57235957 | 214 | if (temp.current_temperature > high_temp) { |
abe97b79 | 215 | high_temp = temp.current_temperature; |
abe97b79 | 216 | } |
217 | } | |
218 | } | |
219 | return high_temp; | |
220 | } | |
221 | ||
222 | // Turn the switch on (true) or off (false) | |
223 | void TemperatureSwitch::set_switch(bool switch_state) | |
224 | { | |
c0c375b0 JM |
225 | if(this->temperatureswitch_state == switch_state) return; |
226 | ||
abe97b79 | 227 | this->temperatureswitch_state = switch_state; |
c0c375b0 JM |
228 | |
229 | if(this->inverted) switch_state= !switch_state; // turn switch on or off inverted | |
230 | ||
231 | bool ok = PublicData::set_value(switch_checksum, this->temperatureswitch_switch_cs, state_checksum, &switch_state); | |
abe97b79 | 232 | if (!ok) { |
c0c375b0 | 233 | THEKERNEL->streams->printf("// Failed changing switch state.\r\n"); |
abe97b79 | 234 | } |
235 | } |