Commit | Line | Data |
---|---|---|
cd011f58 AW |
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 | // TODO : THIS FILE IS LAME, MUST BE MADE MUCH BETTER | |
9 | ||
ded56b35 AW |
10 | #include "mbed.h" |
11 | #include "libs/Module.h" | |
12 | #include "libs/Kernel.h" | |
13 | #include <math.h> | |
14 | #include "TemperatureControl.h" | |
3c132bd0 | 15 | #include "libs/Pin.h" |
ded56b35 | 16 | |
3c132bd0 AW |
17 | TemperatureControl::TemperatureControl(){} |
18 | ||
19 | TemperatureControl::TemperatureControl(uint16_t name){ | |
20 | this->name_checksum = name; | |
ded56b35 AW |
21 | this->error_count = 0; |
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 | |
37 | this->register_for_event(ON_GCODE_EXECUTE); | |
b6c86164 | 38 | this->register_for_event(ON_MAIN_LOOP); |
ded56b35 AW |
39 | |
40 | } | |
41 | ||
3c132bd0 | 42 | void TemperatureControl::on_main_loop(void* argument){ } |
7dd8133c AW |
43 | |
44 | // Get configuration from the config file | |
45 | void TemperatureControl::on_config_reload(void* argument){ | |
46 | ||
3c132bd0 | 47 | this->readings_per_second = this->kernel->config->value(temperature_control_checksum, this->name_checksum, readings_per_second_ckeckusm)->by_default(5)->as_number(); |
7dd8133c AW |
48 | |
49 | // Values are here : http://reprap.org/wiki/Thermistor | |
50 | // TODO: WARNING : THIS WILL CHANGE and backward compatibility will be broken for config files that use this | |
a98f72da AW |
51 | this->r0 = 100000; |
52 | this->t0 = 25; | |
53 | this->beta = 4066; | |
54 | this->vadc = 3.3; | |
55 | this->vcc = 3.3; | |
56 | this->r1 = 0; | |
57 | this->r2 = 4700; | |
58 | ||
3c132bd0 AW |
59 | // Preset values for various common types of thermistors |
60 | ConfigValue* thermistor = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_checksum); | |
a98f72da AW |
61 | if( thermistor->value.compare("EPCOS100K") == 0 ){ |
62 | // Already the default | |
63 | }else if( thermistor->value.compare("RRRF100K") == 0 ){ | |
64 | this->beta = 3960; | |
65 | }else if( thermistor->value.compare("RRRF10K") == 0 ){ | |
66 | this->beta = 3964; | |
67 | this->r0 = 10000; | |
68 | this->r1 = 680; | |
69 | this->r2 = 1600; | |
70 | }else if( thermistor->value.compare("Honeywell100K") == 0 ){ | |
71 | this->beta = 3974; | |
72 | }else if( thermistor->value.compare("Semitec") == 0 ){ | |
73 | this->beta = 4267; | |
74 | } | |
75 | ||
3c132bd0 AW |
76 | // Preset values are overriden by specified values |
77 | this->r0 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r0_ckeckusm )->by_default(100000)->as_number(); // Stated resistance eg. 100K | |
78 | this->t0 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, t0_ckeckusm )->by_default(25 )->as_number() + 273.15; // Temperature at stated resistance, eg. 25C | |
79 | this->beta = this->kernel->config->value(temperature_control_checksum, this->name_checksum, beta_ckeckusm)->by_default(4066 )->as_number(); // Thermistor beta rating. See http://reprap.org/bin/view/Main/MeasuringThermistorBeta | |
80 | this->vadc = this->kernel->config->value(temperature_control_checksum, this->name_checksum, vadc_ckeckusm)->by_default(3.3 )->as_number(); // ADC Reference | |
81 | this->vcc = this->kernel->config->value(temperature_control_checksum, this->name_checksum, vcc_ckeckusm )->by_default(3.3 )->as_number(); // Supply voltage to potential divider | |
82 | this->r1 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r1_ckeckusm )->by_default(0 )->as_number(); | |
83 | this->r2 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r2_ckeckusm )->by_default(4700 )->as_number(); | |
7dd8133c | 84 | |
3c132bd0 AW |
85 | // Thermistor math |
86 | this->k = this->r0 * exp( -this->beta / this->t0 ); | |
87 | if( r1 > 0 ){ this->vs = r1 * this->vcc / ( r1 + r2 ); this->rs = r1 * r2 / ( r1 + r2 ); }else{ this->vs = this->vcc; this->rs = r2; } | |
88 | ||
89 | // Thermistor pin for ADC readings | |
90 | this->thermistor_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_pin_checksum )->required()->as_pin(); | |
91 | this->kernel->adc->enable_pin(this->thermistor_pin); | |
92 | ||
93 | // Heater pin | |
94 | this->heater_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->required()->as_pin()->as_output(); | |
95 | this->heater_pin->set(0); | |
7dd8133c AW |
96 | |
97 | } | |
98 | ||
ded56b35 AW |
99 | void TemperatureControl::on_gcode_execute(void* argument){ |
100 | Gcode* gcode = static_cast<Gcode*>(argument); | |
3c132bd0 | 101 | |
81b547a1 | 102 | // Set temperature without waiting |
ded56b35 AW |
103 | if( gcode->has_letter('M') && gcode->get_value('M') == 104 && gcode->has_letter('S') ){ |
104 | this->set_desired_temperature(gcode->get_value('S')); | |
105 | } | |
106 | ||
81b547a1 AW |
107 | // Set temperature and wait |
108 | if( gcode->has_letter('M') && gcode->get_value('M') == 109 && gcode->has_letter('S') ){ | |
109 | this->set_desired_temperature(gcode->get_value('S')); | |
110 | ||
111 | // Pause | |
112 | this->kernel->pauser->take(); | |
113 | this->waiting = true; | |
114 | ||
115 | } | |
116 | ||
ded56b35 AW |
117 | // Get temperature |
118 | if( gcode->has_letter('M') && gcode->get_value('M') == 105 ){ | |
3c132bd0 | 119 | gcode->stream->printf("get temperature: %f current:%f target:%f \r\n", this->get_temperature(), this->new_thermistor_reading(), this->desired_adc_value ); |
ded56b35 AW |
120 | } |
121 | } | |
122 | ||
123 | void TemperatureControl::set_desired_temperature(double desired_temperature){ | |
124 | this->desired_adc_value = this->temperature_to_adc_value(desired_temperature); | |
ded56b35 AW |
125 | } |
126 | ||
127 | double TemperatureControl::get_temperature(){ | |
128 | double temp = this->new_thermistor_reading() ; | |
129 | return this->adc_value_to_temperature( this->new_thermistor_reading() ); | |
130 | } | |
131 | ||
132 | double TemperatureControl::adc_value_to_temperature(double adc_value){ | |
133 | double v = adc_value * this->vadc; // Convert from 0-1 adc value to voltage | |
134 | double r = this->rs * v / ( this->vs - v ); // Resistance of thermistor | |
135 | return ( this->beta / log( r / this->k )) - 273.15; | |
136 | } | |
137 | ||
138 | double TemperatureControl::temperature_to_adc_value(double temperature){ | |
139 | double r = this->r0 * exp( this->beta * ( 1 / (temperature + 273.15) -1 / this->t0 ) ); // Resistance of the thermistor | |
140 | double v = this->vs * r / ( this->rs + r ); // Voltage at the potential divider | |
141 | return v / this->vadc * 1.00000; // The ADC reading | |
142 | } | |
143 | ||
144 | void TemperatureControl::thermistor_read_tick(){ | |
ded56b35 | 145 | if( this->desired_adc_value != UNDEFINED ){ |
b6c86164 | 146 | if( this->new_thermistor_reading() > this->desired_adc_value ){ |
3c132bd0 | 147 | this->heater_pin->set(1); |
ded56b35 | 148 | }else{ |
3c132bd0 | 149 | this->heater_pin->set(0); |
81b547a1 AW |
150 | if( this->waiting ){ |
151 | this->kernel->pauser->release(); | |
152 | this->waiting = false; | |
153 | } | |
ded56b35 AW |
154 | } |
155 | } | |
ded56b35 AW |
156 | } |
157 | ||
158 | double TemperatureControl::new_thermistor_reading(){ | |
3c132bd0 AW |
159 | |
160 | double new_reading = double( double(this->kernel->adc->read(this->thermistor_pin) / double(1<<12) ) ); | |
ded56b35 AW |
161 | |
162 | if( this->queue.size() < 15 ){ | |
163 | this->queue.push_back( new_reading ); | |
ded56b35 AW |
164 | return new_reading; |
165 | }else{ | |
166 | double current_temp = this->average_adc_reading(); | |
167 | double error = fabs(new_reading - current_temp); | |
168 | if( error < 0.1 ){ | |
169 | this->error_count = 0; | |
170 | double test; | |
171 | this->queue.pop_front(test); | |
172 | this->queue.push_back( new_reading ); | |
173 | }else{ | |
174 | this->error_count++; | |
175 | if( this->error_count > 4 ){ | |
176 | double test; | |
177 | this->queue.pop_front(test); | |
178 | } | |
179 | } | |
180 | return current_temp; | |
181 | } | |
182 | } | |
183 | ||
184 | ||
185 | double TemperatureControl::average_adc_reading(){ | |
186 | double total; | |
187 | int j=0; | |
188 | int reading_index = this->queue.head; | |
189 | while( reading_index != this->queue.tail ){ | |
190 | j++; | |
191 | total += this->queue.buffer[reading_index]; | |
192 | reading_index = this->queue.next_block_index( reading_index ); | |
193 | } | |
194 | return total / j; | |
195 | } | |
196 | ||
197 | ||
198 |