Commit | Line | Data |
---|---|---|
df27a6a3 | 1 | /* |
cd011f58 AW |
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. | |
df27a6a3 | 5 | You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. |
cd011f58 AW |
6 | */ |
7 | ||
7b49793d | 8 | // TODO : THIS FILE IS LAME, MUST BE MADE MUCH BETTER |
cd011f58 | 9 | |
ded56b35 AW |
10 | #include "libs/Module.h" |
11 | #include "libs/Kernel.h" | |
12 | #include <math.h> | |
13 | #include "TemperatureControl.h" | |
3c132bd0 | 14 | #include "libs/Pin.h" |
ded56b35 | 15 | |
3c132bd0 AW |
16 | TemperatureControl::TemperatureControl(){} |
17 | ||
18 | TemperatureControl::TemperatureControl(uint16_t name){ | |
19 | this->name_checksum = name; | |
df27a6a3 | 20 | this->error_count = 0; |
4464301d | 21 | this->waiting = false; |
ded56b35 AW |
22 | } |
23 | ||
24 | void TemperatureControl::on_module_loaded(){ | |
25 | ||
81b547a1 | 26 | // We start not desiring any temp |
ded56b35 AW |
27 | this->desired_adc_value = UNDEFINED; |
28 | ||
29 | // Settings | |
7dd8133c | 30 | this->on_config_reload(this); |
ded56b35 AW |
31 | |
32 | this->acceleration_factor = 10; | |
33 | ||
d9ebc974 | 34 | this->kernel->slow_ticker->attach( 20, this, &TemperatureControl::thermistor_read_tick ); |
ded56b35 AW |
35 | |
36 | // Register for events | |
df27a6a3 | 37 | this->register_for_event(ON_GCODE_EXECUTE); |
b0be67b5 | 38 | this->register_for_event(ON_GCODE_RECEIVED); |
df27a6a3 | 39 | this->register_for_event(ON_MAIN_LOOP); |
ded56b35 AW |
40 | |
41 | } | |
42 | ||
3c132bd0 | 43 | void TemperatureControl::on_main_loop(void* argument){ } |
7dd8133c AW |
44 | |
45 | // Get configuration from the config file | |
46 | void TemperatureControl::on_config_reload(void* argument){ | |
47 | ||
1306ba99 AW |
48 | // General config |
49 | this->set_m_code = this->kernel->config->value(temperature_control_checksum, this->name_checksum, set_m_code_checksum)->by_default(104)->as_number(); | |
50 | this->set_and_wait_m_code = this->kernel->config->value(temperature_control_checksum, this->name_checksum, set_and_wait_m_code_checksum)->by_default(109)->as_number(); | |
51 | this->get_m_code = this->kernel->config->value(temperature_control_checksum, this->name_checksum, get_m_code_checksum)->by_default(105)->as_number(); | |
8c309ca9 | 52 | this->readings_per_second = this->kernel->config->value(temperature_control_checksum, this->name_checksum, readings_per_second_checksum)->by_default(5)->as_number(); |
7dd8133c | 53 | |
b0be67b5 MM |
54 | this->designator = this->kernel->config->value(temperature_control_checksum, this->name_checksum, designator_checksum)->by_default(string("T"))->as_string(); |
55 | ||
7dd8133c | 56 | // Values are here : http://reprap.org/wiki/Thermistor |
423df6df AW |
57 | this->r0 = 100000; |
58 | this->t0 = 25; | |
a98f72da AW |
59 | this->beta = 4066; |
60 | this->vadc = 3.3; | |
423df6df AW |
61 | this->vcc = 3.3; |
62 | this->r1 = 0; | |
63 | this->r2 = 4700; | |
a98f72da | 64 | |
3c132bd0 | 65 | // Preset values for various common types of thermistors |
df27a6a3 | 66 | ConfigValue* thermistor = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_checksum); |
423df6df AW |
67 | if( thermistor->value.compare("EPCOS100K" ) == 0 ){ // Default |
68 | }else if( thermistor->value.compare("RRRF100K" ) == 0 ){ this->beta = 3960; | |
69 | }else if( thermistor->value.compare("RRRF10K" ) == 0 ){ this->beta = 3964; this->r0 = 10000; this->r1 = 680; this->r2 = 1600; | |
70 | }else if( thermistor->value.compare("Honeywell100K") == 0 ){ this->beta = 3974; | |
71 | }else if( thermistor->value.compare("Semitec" ) == 0 ){ this->beta = 4267; } | |
a98f72da | 72 | |
3c132bd0 | 73 | // Preset values are overriden by specified values |
8c309ca9 L |
74 | this->r0 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r0_checksum )->by_default(100000)->as_number(); // Stated resistance eg. 100K |
75 | this->t0 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, t0_checksum )->by_default(25 )->as_number() + 273.15; // Temperature at stated resistance, eg. 25C | |
76 | this->beta = this->kernel->config->value(temperature_control_checksum, this->name_checksum, beta_checksum)->by_default(4066 )->as_number(); // Thermistor beta rating. See http://reprap.org/bin/view/Main/MeasuringThermistorBeta | |
77 | this->vadc = this->kernel->config->value(temperature_control_checksum, this->name_checksum, vadc_checksum)->by_default(3.3 )->as_number(); // ADC Reference | |
78 | this->vcc = this->kernel->config->value(temperature_control_checksum, this->name_checksum, vcc_checksum )->by_default(3.3 )->as_number(); // Supply voltage to potential divider | |
79 | this->r1 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r1_checksum )->by_default(0 )->as_number(); | |
80 | this->r2 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r2_checksum )->by_default(4700 )->as_number(); | |
7dd8133c | 81 | |
df27a6a3 | 82 | // Thermistor math |
3c132bd0 | 83 | this->k = this->r0 * exp( -this->beta / this->t0 ); |
df27a6a3 | 84 | if( r1 > 0 ){ this->vs = r1 * this->vcc / ( r1 + r2 ); this->rs = r1 * r2 / ( r1 + r2 ); }else{ this->vs = this->vcc; this->rs = r2; } |
3c132bd0 AW |
85 | |
86 | // Thermistor pin for ADC readings | |
87 | this->thermistor_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_pin_checksum )->required()->as_pin(); | |
88 | this->kernel->adc->enable_pin(this->thermistor_pin); | |
89 | ||
90 | // Heater pin | |
91 | this->heater_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->required()->as_pin()->as_output(); | |
92 | this->heater_pin->set(0); | |
7dd8133c AW |
93 | |
94 | } | |
95 | ||
b2b0b56d AW |
96 | //#pragma GCC push_options |
97 | //#pragma GCC optimize ("O0") | |
db453125 | 98 | |
b0be67b5 MM |
99 | void TemperatureControl::on_gcode_received(void* argument) |
100 | { | |
101 | Gcode* gcode = static_cast<Gcode*>(argument); | |
102 | if (gcode->has_m) | |
103 | { | |
104 | // Get temperature | |
105 | if( gcode->m == this->get_m_code ){ | |
106 | // gcode->stream->printf("get temperature: %f current:%f target:%f bare_value:%u \r\n", this->get_temperature(), this->new_thermistor_reading(), this->desired_adc_value, this->kernel->adc->read(this->thermistor_pin) ); | |
107 | gcode->stream->printf("%s:%3.1f /%3.1f ", this->designator.c_str(), this->get_temperature(), ((this->desired_adc_value == UNDEFINED)?0.0:this->adc_value_to_temperature(this->desired_adc_value))); | |
ac9f9b7a | 108 | gcode->add_nl = true; |
b0be67b5 MM |
109 | } |
110 | } | |
111 | } | |
db453125 | 112 | |
ded56b35 AW |
113 | void TemperatureControl::on_gcode_execute(void* argument){ |
114 | Gcode* gcode = static_cast<Gcode*>(argument); | |
e6b5ae25 AW |
115 | if( gcode->has_m){ |
116 | // Set temperature without waiting | |
117 | if( gcode->m == this->set_m_code && gcode->has_letter('S') ){ | |
118 | //gcode->stream->printf("setting to %f meaning %u \r\n", gcode->get_value('S'), this->temperature_to_adc_value( gcode->get_value('S') ) ); | |
eabf7a9b MM |
119 | if (gcode->get_value('S') == 0) |
120 | { | |
121 | this->desired_adc_value = UNDEFINED; | |
122 | this->heater_pin->set(0); | |
123 | } | |
124 | else | |
125 | { | |
df27a6a3 MM |
126 | this->set_desired_temperature(gcode->get_value('S')); |
127 | } | |
eabf7a9b | 128 | } |
e6b5ae25 AW |
129 | // Set temperature and wait |
130 | if( gcode->m == this->set_and_wait_m_code && gcode->has_letter('S') ){ | |
eabf7a9b MM |
131 | if (gcode->get_value('S') == 0) |
132 | { | |
133 | this->desired_adc_value = UNDEFINED; | |
134 | this->heater_pin->set(0); | |
135 | } | |
136 | else | |
137 | { | |
e6b5ae25 | 138 | this->set_desired_temperature(gcode->get_value('S')); |
df27a6a3 MM |
139 | // Pause |
140 | this->kernel->pauser->take(); | |
141 | this->waiting = true; | |
142 | } | |
eabf7a9b | 143 | } |
df27a6a3 | 144 | } |
ded56b35 AW |
145 | } |
146 | ||
b2b0b56d | 147 | //#pragma GCC pop_options |
db453125 AW |
148 | |
149 | ||
ded56b35 AW |
150 | void TemperatureControl::set_desired_temperature(double desired_temperature){ |
151 | this->desired_adc_value = this->temperature_to_adc_value(desired_temperature); | |
ded56b35 AW |
152 | } |
153 | ||
154 | double TemperatureControl::get_temperature(){ | |
ded56b35 AW |
155 | return this->adc_value_to_temperature( this->new_thermistor_reading() ); |
156 | } | |
157 | ||
158 | double TemperatureControl::adc_value_to_temperature(double adc_value){ | |
159 | double v = adc_value * this->vadc; // Convert from 0-1 adc value to voltage | |
160 | double r = this->rs * v / ( this->vs - v ); // Resistance of thermistor | |
161 | return ( this->beta / log( r / this->k )) - 273.15; | |
162 | } | |
163 | ||
164 | double TemperatureControl::temperature_to_adc_value(double temperature){ | |
df27a6a3 | 165 | double r = this->r0 * exp( this->beta * ( 1 / (temperature + 273.15) -1 / this->t0 ) ); // Resistance of the thermistor |
ded56b35 AW |
166 | double v = this->vs * r / ( this->rs + r ); // Voltage at the potential divider |
167 | return v / this->vadc * 1.00000; // The ADC reading | |
168 | } | |
169 | ||
8b8b3339 | 170 | uint32_t TemperatureControl::thermistor_read_tick(uint32_t dummy){ |
ded56b35 | 171 | if( this->desired_adc_value != UNDEFINED ){ |
36a3f3f0 MM |
172 | double r = this->new_thermistor_reading(); |
173 | if ((r > 0.01) && | |
174 | (r < 0.99) && | |
175 | (r > this->desired_adc_value)) | |
176 | { | |
df27a6a3 | 177 | this->heater_pin->set(1); |
36a3f3f0 MM |
178 | } |
179 | else | |
180 | { | |
181 | if (((r <= 0.01) || (r >= 0.99)) && (this->desired_adc_value != UNDEFINED)) | |
182 | { | |
183 | this->kernel->streams->printf("MINTEMP triggered on P%d.%d! check your thermistors!\n", this->thermistor_pin->port_number, this->thermistor_pin->pin); | |
184 | this->desired_adc_value = UNDEFINED; | |
185 | } | |
df27a6a3 | 186 | this->heater_pin->set(0); |
36a3f3f0 MM |
187 | if (this->waiting) |
188 | { | |
81b547a1 | 189 | this->kernel->pauser->release(); |
df27a6a3 | 190 | this->waiting = false; |
81b547a1 | 191 | } |
ded56b35 AW |
192 | } |
193 | } | |
281967e4 | 194 | return 0; |
ded56b35 AW |
195 | } |
196 | ||
197 | double TemperatureControl::new_thermistor_reading(){ | |
3c132bd0 AW |
198 | |
199 | double new_reading = double( double(this->kernel->adc->read(this->thermistor_pin) / double(1<<12) ) ); | |
ded56b35 AW |
200 | |
201 | if( this->queue.size() < 15 ){ | |
202 | this->queue.push_back( new_reading ); | |
ded56b35 AW |
203 | return new_reading; |
204 | }else{ | |
205 | double current_temp = this->average_adc_reading(); | |
df27a6a3 | 206 | double error = fabs(new_reading - current_temp); |
ded56b35 AW |
207 | if( error < 0.1 ){ |
208 | this->error_count = 0; | |
209 | double test; | |
df27a6a3 | 210 | this->queue.pop_front(test); |
ded56b35 AW |
211 | this->queue.push_back( new_reading ); |
212 | }else{ | |
213 | this->error_count++; | |
214 | if( this->error_count > 4 ){ | |
215 | double test; | |
df27a6a3 | 216 | this->queue.pop_front(test); |
ded56b35 | 217 | } |
df27a6a3 | 218 | } |
ded56b35 AW |
219 | return current_temp; |
220 | } | |
221 | } | |
222 | ||
223 | ||
224 | double TemperatureControl::average_adc_reading(){ | |
76bb4c37 BG |
225 | double total = 0; |
226 | int j = 0; | |
ded56b35 AW |
227 | int reading_index = this->queue.head; |
228 | while( reading_index != this->queue.tail ){ | |
229 | j++; | |
230 | total += this->queue.buffer[reading_index]; | |
231 | reading_index = this->queue.next_block_index( reading_index ); | |
232 | } | |
233 | return total / j; | |
234 | } | |
235 | ||
236 | ||
237 |