Merge pull request #438 from mhackney/edge
[clinton/Smoothieware.git] / src / modules / tools / temperatureswitch / TemperatureSwitch.cpp
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"
29
30 #define temperatureswitch_checksum CHECKSUM("temperatureswitch")
31 #define temperatureswitch_enable_checksum CHECKSUM("enable")
32 #define temperatureswitch_hotend_checksum CHECKSUM("hotend")
33 #define temperatureswitch_threshold_temp_checksum CHECKSUM("threshold_temp")
34 #define temperatureswitch_type_checksum CHECKSUM("type")
35 #define temperatureswitch_heatup_poll_checksum CHECKSUM("heatup_poll")
36 #define temperatureswitch_cooldown_poll_checksum CHECKSUM("cooldown_poll")
37
38 TemperatureSwitch::TemperatureSwitch()
39 {
40 }
41
42 // Load module
43 void TemperatureSwitch::on_module_loaded()
44 {
45 // free up space if not loaded
46 if (!THEKERNEL->config->value(temperatureswitch_checksum, temperatureswitch_hotend_checksum, temperatureswitch_enable_checksum)->by_default(false)->as_bool()) {
47 delete this;
48 return;
49 }
50
51 // settings
52 this->on_config_reload(this);
53 }
54
55 // Get config
56 void TemperatureSwitch::on_config_reload(void *argument)
57 {
58 // get the list of temperature controllers and remove any that fon't have designator == "T"
59 vector<uint16_t> controller_list;
60 THEKERNEL->config->get_module_list(&controller_list, temperature_control_checksum);
61
62 void *returned_temp;
63 for (auto controller : controller_list) {
64 bool temp_ok = PublicData::get_value(temperature_control_checksum, controller, current_temperature_checksum, &returned_temp);
65 if (temp_ok) {
66 struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_temp);
67 // if the controller is a hotend (designator == "T") then keep it
68 if (temp.designator.substr(0, 1) == "T") {
69 temp_controllers.push_back(controller);
70 }
71 }
72 }
73
74 // if we don't have any controllers, free up space
75 if (temp_controllers.empty()) {
76 delete this;
77 return;
78 }
79
80 // load settings from config file
81 this->temperatureswitch_state = false;
82 this->temperatureswitch_type = THEKERNEL->config->value(temperatureswitch_checksum, temperatureswitch_hotend_checksum, temperatureswitch_type_checksum)->by_default("")->as_string();
83 this->temperatureswitch_threshold_temp = THEKERNEL->config->value(temperatureswitch_checksum, temperatureswitch_hotend_checksum, temperatureswitch_threshold_temp_checksum)->by_default(50.0f)->as_number();
84
85 // these are to tune the heatup and cooldown polling frequencies
86 this->temperatureswitch_heatup_poll = THEKERNEL->config->value(temperatureswitch_checksum, temperatureswitch_hotend_checksum, temperatureswitch_heatup_poll_checksum)->by_default(15)->as_number();
87 this->temperatureswitch_cooldown_poll = THEKERNEL->config->value(temperatureswitch_checksum, temperatureswitch_hotend_checksum, temperatureswitch_cooldown_poll_checksum)->by_default(60)->as_number();
88
89 second_counter = 0;
90 current_delay = this->temperatureswitch_heatup_poll;
91
92 // Register for events
93 this->register_for_event(ON_SECOND_TICK);
94 }
95
96 // Called once a second but we only need to service on the cooldown and heatup poll intervals
97 void TemperatureSwitch::on_second_tick(void *argument)
98 {
99 second_counter++;
100 if (second_counter < current_delay) {
101 return;
102 } else {
103 second_counter = 0;
104 float current_temp = this->get_highest_temperature();
105
106 if (current_temp >= this->temperatureswitch_threshold_temp) {
107 // temp >= threshold temp, turn the cooler switch on if it isn't already
108 if (!temperatureswitch_state) {
109 set_switch(true);
110 current_delay = temperatureswitch_cooldown_poll;
111 }
112 } else {
113 // temp < threshold temp, turn the cooler switch off if it isn't already
114 if (temperatureswitch_state) {
115 set_switch(false);
116 current_delay = temperatureswitch_heatup_poll;
117 }
118 }
119 }
120 }
121
122 // Get the highest temperature from the set of temperature controllers
123 float TemperatureSwitch::get_highest_temperature()
124 {
125 void *returned_temp;
126 float high_temp = 0.0;
127
128 for (auto controller : temp_controllers) {
129 bool temp_ok = PublicData::get_value(temperature_control_checksum, controller, current_temperature_checksum, &returned_temp);
130 if (temp_ok) {
131 struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_temp);
132 // check if this controller's temp is the highest and save it if so
133 if (temp.current_temperature > high_temp && temp.designator.substr(0, 1) == "T") {
134 high_temp = temp.current_temperature;
135 }
136 }
137 }
138 return high_temp;
139 }
140
141 // Turn the switch on (true) or off (false)
142 void TemperatureSwitch::set_switch(bool switch_state)
143 {
144 this->temperatureswitch_state = switch_state;
145 bool ok = PublicData::set_value(switch_checksum, get_checksum(this->temperatureswitch_type), state_checksum, &this->temperatureswitch_state);
146 if (!ok) {
147 THEKERNEL->streams->printf("Failed changing switch state.\r\n");
148 }
149 }