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 "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; | |
ded56b35 AW |
20 | this->error_count = 0; |
21 | } | |
22 | ||
23 | void TemperatureControl::on_module_loaded(){ | |
24 | ||
81b547a1 | 25 | // We start not desiring any temp |
ded56b35 AW |
26 | this->desired_adc_value = UNDEFINED; |
27 | ||
28 | // Settings | |
7dd8133c | 29 | this->on_config_reload(this); |
ded56b35 AW |
30 | |
31 | this->acceleration_factor = 10; | |
32 | ||
d9ebc974 | 33 | this->kernel->slow_ticker->attach( 20, this, &TemperatureControl::thermistor_read_tick ); |
ded56b35 AW |
34 | |
35 | // Register for events | |
36 | this->register_for_event(ON_GCODE_EXECUTE); | |
b6c86164 | 37 | this->register_for_event(ON_MAIN_LOOP); |
ded56b35 AW |
38 | |
39 | } | |
40 | ||
3c132bd0 | 41 | void TemperatureControl::on_main_loop(void* argument){ } |
7dd8133c AW |
42 | |
43 | // Get configuration from the config file | |
44 | void TemperatureControl::on_config_reload(void* argument){ | |
45 | ||
3c132bd0 | 46 | 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 |
47 | |
48 | // Values are here : http://reprap.org/wiki/Thermistor | |
423df6df AW |
49 | this->r0 = 100000; |
50 | this->t0 = 25; | |
a98f72da AW |
51 | this->beta = 4066; |
52 | this->vadc = 3.3; | |
423df6df AW |
53 | this->vcc = 3.3; |
54 | this->r1 = 0; | |
55 | this->r2 = 4700; | |
a98f72da | 56 | |
3c132bd0 AW |
57 | // Preset values for various common types of thermistors |
58 | ConfigValue* thermistor = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_checksum); | |
423df6df AW |
59 | if( thermistor->value.compare("EPCOS100K" ) == 0 ){ // Default |
60 | }else if( thermistor->value.compare("RRRF100K" ) == 0 ){ this->beta = 3960; | |
61 | }else if( thermistor->value.compare("RRRF10K" ) == 0 ){ this->beta = 3964; this->r0 = 10000; this->r1 = 680; this->r2 = 1600; | |
62 | }else if( thermistor->value.compare("Honeywell100K") == 0 ){ this->beta = 3974; | |
63 | }else if( thermistor->value.compare("Semitec" ) == 0 ){ this->beta = 4267; } | |
a98f72da | 64 | |
3c132bd0 AW |
65 | // Preset values are overriden by specified values |
66 | this->r0 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r0_ckeckusm )->by_default(100000)->as_number(); // Stated resistance eg. 100K | |
67 | 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 | |
68 | 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 | |
69 | this->vadc = this->kernel->config->value(temperature_control_checksum, this->name_checksum, vadc_ckeckusm)->by_default(3.3 )->as_number(); // ADC Reference | |
70 | 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 | |
71 | this->r1 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r1_ckeckusm )->by_default(0 )->as_number(); | |
72 | this->r2 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r2_ckeckusm )->by_default(4700 )->as_number(); | |
7dd8133c | 73 | |
3c132bd0 AW |
74 | // Thermistor math |
75 | this->k = this->r0 * exp( -this->beta / this->t0 ); | |
76 | if( r1 > 0 ){ this->vs = r1 * this->vcc / ( r1 + r2 ); this->rs = r1 * r2 / ( r1 + r2 ); }else{ this->vs = this->vcc; this->rs = r2; } | |
77 | ||
78 | // Thermistor pin for ADC readings | |
79 | this->thermistor_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_pin_checksum )->required()->as_pin(); | |
80 | this->kernel->adc->enable_pin(this->thermistor_pin); | |
81 | ||
82 | // Heater pin | |
83 | this->heater_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->required()->as_pin()->as_output(); | |
84 | this->heater_pin->set(0); | |
7dd8133c AW |
85 | |
86 | } | |
87 | ||
ded56b35 AW |
88 | void TemperatureControl::on_gcode_execute(void* argument){ |
89 | Gcode* gcode = static_cast<Gcode*>(argument); | |
3c132bd0 | 90 | |
81b547a1 | 91 | // Set temperature without waiting |
ded56b35 AW |
92 | if( gcode->has_letter('M') && gcode->get_value('M') == 104 && gcode->has_letter('S') ){ |
93 | this->set_desired_temperature(gcode->get_value('S')); | |
94 | } | |
95 | ||
81b547a1 AW |
96 | // Set temperature and wait |
97 | if( gcode->has_letter('M') && gcode->get_value('M') == 109 && gcode->has_letter('S') ){ | |
98 | this->set_desired_temperature(gcode->get_value('S')); | |
99 | ||
100 | // Pause | |
101 | this->kernel->pauser->take(); | |
102 | this->waiting = true; | |
103 | ||
104 | } | |
105 | ||
ded56b35 AW |
106 | // Get temperature |
107 | if( gcode->has_letter('M') && gcode->get_value('M') == 105 ){ | |
3c132bd0 | 108 | 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 |
109 | } |
110 | } | |
111 | ||
112 | void TemperatureControl::set_desired_temperature(double desired_temperature){ | |
113 | this->desired_adc_value = this->temperature_to_adc_value(desired_temperature); | |
ded56b35 AW |
114 | } |
115 | ||
116 | double TemperatureControl::get_temperature(){ | |
117 | double temp = this->new_thermistor_reading() ; | |
118 | return this->adc_value_to_temperature( this->new_thermistor_reading() ); | |
119 | } | |
120 | ||
121 | double TemperatureControl::adc_value_to_temperature(double adc_value){ | |
122 | double v = adc_value * this->vadc; // Convert from 0-1 adc value to voltage | |
123 | double r = this->rs * v / ( this->vs - v ); // Resistance of thermistor | |
124 | return ( this->beta / log( r / this->k )) - 273.15; | |
125 | } | |
126 | ||
127 | double TemperatureControl::temperature_to_adc_value(double temperature){ | |
128 | double r = this->r0 * exp( this->beta * ( 1 / (temperature + 273.15) -1 / this->t0 ) ); // Resistance of the thermistor | |
129 | double v = this->vs * r / ( this->rs + r ); // Voltage at the potential divider | |
130 | return v / this->vadc * 1.00000; // The ADC reading | |
131 | } | |
132 | ||
133 | void TemperatureControl::thermistor_read_tick(){ | |
ded56b35 | 134 | if( this->desired_adc_value != UNDEFINED ){ |
b6c86164 | 135 | if( this->new_thermistor_reading() > this->desired_adc_value ){ |
3c132bd0 | 136 | this->heater_pin->set(1); |
ded56b35 | 137 | }else{ |
3c132bd0 | 138 | this->heater_pin->set(0); |
81b547a1 AW |
139 | if( this->waiting ){ |
140 | this->kernel->pauser->release(); | |
141 | this->waiting = false; | |
142 | } | |
ded56b35 AW |
143 | } |
144 | } | |
ded56b35 AW |
145 | } |
146 | ||
147 | double TemperatureControl::new_thermistor_reading(){ | |
3c132bd0 AW |
148 | |
149 | double new_reading = double( double(this->kernel->adc->read(this->thermistor_pin) / double(1<<12) ) ); | |
ded56b35 AW |
150 | |
151 | if( this->queue.size() < 15 ){ | |
152 | this->queue.push_back( new_reading ); | |
ded56b35 AW |
153 | return new_reading; |
154 | }else{ | |
155 | double current_temp = this->average_adc_reading(); | |
156 | double error = fabs(new_reading - current_temp); | |
157 | if( error < 0.1 ){ | |
158 | this->error_count = 0; | |
159 | double test; | |
160 | this->queue.pop_front(test); | |
161 | this->queue.push_back( new_reading ); | |
162 | }else{ | |
163 | this->error_count++; | |
164 | if( this->error_count > 4 ){ | |
165 | double test; | |
166 | this->queue.pop_front(test); | |
167 | } | |
168 | } | |
169 | return current_temp; | |
170 | } | |
171 | } | |
172 | ||
173 | ||
174 | double TemperatureControl::average_adc_reading(){ | |
175 | double total; | |
176 | int j=0; | |
177 | int reading_index = this->queue.head; | |
178 | while( reading_index != this->queue.tail ){ | |
179 | j++; | |
180 | total += this->queue.buffer[reading_index]; | |
181 | reading_index = this->queue.next_block_index( reading_index ); | |
182 | } | |
183 | return total / j; | |
184 | } | |
185 | ||
186 | ||
187 |