removed most mbed.h includes
[clinton/Smoothieware.git] / src / modules / tools / extruder / Extruder.cpp
CommitLineData
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
4cff3ded
AW
9#include "libs/Module.h"
10#include "libs/Kernel.h"
13e4a3f9 11#include "modules/robot/Player.h"
4cff3ded
AW
12#include "modules/robot/Block.h"
13#include "modules/tools/extruder/Extruder.h"
14
0eb11a06 15Extruder::Extruder(PinName stppin, PinName dirpin) : step_pin(stppin), dir_pin(dirpin) {
436a2cd1 16 this->absolute_mode = true;
0eb11a06 17 this->direction = 1;
ded56b35 18 this->acceleration_lock = false;
d9ebc974
AW
19 this->step_counter = 0;
20 this->counter_increment = 0;
81b547a1 21 this->paused = false;
436a2cd1 22}
4cff3ded
AW
23
24void Extruder::on_module_loaded() {
f5598f5b 25
ded56b35 26 // Do not do anything if not enabledd
436a2cd1 27 if( this->kernel->config->value( extruder_module_enable_checksum )->by_default(false)->as_bool() == false ){ return; }
f5598f5b 28
4cff3ded 29 // Settings
da24d6ae 30 this->on_config_reload(this);
4cff3ded
AW
31
32 // We work on the same Block as Stepper, so we need to know when it gets a new one and drops one
33 this->register_for_event(ON_BLOCK_BEGIN);
34 this->register_for_event(ON_BLOCK_END);
436a2cd1 35 this->register_for_event(ON_GCODE_EXECUTE);
81b547a1
AW
36 this->register_for_event(ON_PLAY);
37 this->register_for_event(ON_PAUSE);
38
ded56b35 39 // Start values
4cff3ded
AW
40 this->start_position = 0;
41 this->target_position = 0;
42 this->current_position = 0;
43 this->current_block = NULL;
ded56b35
AW
44 this->mode = OFF;
45
ded56b35
AW
46 // Update speed every *acceleration_ticks_per_second*
47 // TODO: Make this an independent setting
d9ebc974 48 this->kernel->slow_ticker->attach( this->kernel->stepper->acceleration_ticks_per_second , this, &Extruder::acceleration_tick );
ded56b35 49
3b1e82d2
AW
50 // Initiate main_interrupt timer and step reset timer
51 this->kernel->step_ticker->attach( this, &Extruder::stepping_tick );
52 this->kernel->step_ticker->reset_attach( this, &Extruder::reset_step_pin );
53
4cff3ded
AW
54}
55
2bb8b390 56// Get config
da24d6ae 57void Extruder::on_config_reload(void* argument){
dd3b416b
AW
58 this->microseconds_per_step_pulse = this->kernel->config->value(microseconds_per_step_pulse_ckeckusm)->by_default(5)->as_number();
59 this->steps_per_millimeter = this->kernel->config->value(steps_per_millimeter_checksum )->by_default(1)->as_number();
60 this->feed_rate = this->kernel->config->value(default_feed_rate_checksum )->by_default(1)->as_number();
61 this->acceleration = this->kernel->config->value(acceleration_checksum )->by_default(1)->as_number();
436a2cd1
AW
62}
63
81b547a1
AW
64
65// When the play/pause button is set to pause, or a module calls the ON_PAUSE event
66void Extruder::on_pause(void* argument){
67 this->paused = true;
68}
69
70// When the play/pause button is set to play, or a module calls the ON_PLAY event
71void Extruder::on_play(void* argument){
72 this->paused = false;
73}
74
75
76
ded56b35 77// Compute extrusion speed based on parameters and gcode distance of travel
436a2cd1
AW
78void Extruder::on_gcode_execute(void* argument){
79 Gcode* gcode = static_cast<Gcode*>(argument);
ded56b35 80
436a2cd1
AW
81 // Absolute/relative mode
82 if( gcode->has_letter('M')){
83 int code = gcode->get_value('M');
84 if( code == 82 ){ this->absolute_mode == true; }
85 if( code == 83 ){ this->absolute_mode == false; }
86 }
ded56b35
AW
87
88 // The mode is OFF by default, and SOLO or FOLLOW only if we need to extrude
89 this->mode = OFF;
90
1a2d88eb 91 if( gcode->has_letter('G') ){
ded56b35 92 // G92: Reset extruder position
1a2d88eb 93 if( gcode->get_value('G') == 92 ){
1a2d88eb 94 if( gcode->has_letter('E') ){
e2b4a32b 95 this->current_position = gcode->get_value('E');
1a2d88eb
AW
96 this->target_position = this->current_position;
97 this->start_position = this->current_position;
98 }
436a2cd1 99 }else{
ded56b35 100 // Extrusion length from 'G' Gcode
1a2d88eb 101 if( gcode->has_letter('E' )){
ded56b35
AW
102 // Get relative extrusion distance depending on mode ( in absolute mode we must substract target_position )
103 double relative_extrusion_distance = gcode->get_value('E');
104 if( this->absolute_mode == true ){ relative_extrusion_distance = relative_extrusion_distance - this->target_position; }
105
106 // If the robot is moving, we follow it's movement, otherwise, we move alone
107 if( fabs(gcode->millimeters_of_travel) < 0.0001 ){ // With floating numbers, we can have 0 != 0 ... beeeh
108 this->mode = SOLO;
109 this->travel_distance = relative_extrusion_distance;
110 if( gcode->has_letter('F') ){ this->feed_rate = gcode->get_value('F'); }
1a2d88eb 111 }else{
ded56b35
AW
112 this->mode = FOLLOW;
113 // We move proportionally to the robot's movement
114 this->travel_ratio = relative_extrusion_distance / gcode->millimeters_of_travel;
1a2d88eb 115 }
ded56b35 116 }
1a2d88eb 117 }
1a2d88eb 118 }
ded56b35 119
da24d6ae
AW
120}
121
ded56b35 122// When a new block begins, either follow the robot, or step by ourselves ( or stay back and do nothing )
4cff3ded
AW
123void Extruder::on_block_begin(void* argument){
124 Block* block = static_cast<Block*>(argument);
ded56b35
AW
125 if( this->mode == SOLO ){
126 // In solo mode we take the block so we can move even if the stepper has nothing to do
127 block->take();
1a2d88eb 128 this->current_block = block;
e2b4a32b 129 this->start_position = this->target_position;
0eb11a06 130 this->target_position = this->start_position + this->travel_distance ;
ded56b35
AW
131 this->travel_ratio = 0.2; // TODO : Make a real acceleration thing
132 if( this->target_position > this->current_position ){ this->direction = 1; }else if( this->target_position < this->current_position ){ this->direction = -1; }
133 this->set_speed(int(floor((this->feed_rate/60)*this->steps_per_millimeter)));//Speed in steps per second
134 }else if( this->mode == FOLLOW ){
1a2d88eb
AW
135 // In non-solo mode, we just follow the stepper module
136 this->current_block = block;
e2b4a32b
AW
137 this->start_position = this->target_position;
138 this->target_position = this->start_position + ( this->current_block->millimeters * this->travel_ratio );
ded56b35
AW
139 if( this->target_position > this->current_position ){ this->direction = 1; }else if( this->target_position < this->current_position ){ this->direction = -1; }
140 this->acceleration_tick();
1a2d88eb 141 }
e2b4a32b 142
4cff3ded
AW
143}
144
ded56b35 145// When a block ends, pause the stepping interrupt
4cff3ded
AW
146void Extruder::on_block_end(void* argument){
147 Block* block = static_cast<Block*>(argument);
148 this->current_block = NULL;
149}
150
ded56b35
AW
151// Called periodically to change the speed to match acceleration or to match the speed of the robot
152void Extruder::acceleration_tick(){
1a2d88eb 153
ded56b35 154 // Avoid trying to work when we really shouldn't ( between blocks or re-entry )
81b547a1 155 if( this->current_block == NULL || this->acceleration_lock || this->paused ){ return; }
ded56b35
AW
156 this->acceleration_lock = true;
157
158 // In solo mode, we mode independently from the robot
159 if( this->mode == SOLO ){
160 // TODO : Do real acceleration here
161 this->travel_ratio += 0.03;
162 if( this->travel_ratio > 1 ){ this->travel_ratio = 1; }
163 this->set_speed( int(floor(((this->feed_rate/60)*this->steps_per_millimeter)*this->travel_ratio)) ); // Speed in steps per second
164
165 // In follow mode we match the speed of the robot, + eventually advance
166 }else if( this->mode == FOLLOW ){
167 Stepper* stepper = this->kernel->stepper; // Just for convenience
168
169 // Strategy :
170 // * Find where in the block will the stepper be at the next tick ( if the block will have ended then, don't change speed )
171 // * Find what position this is for us
172 // * Find what speed we must go at to be at that position for the next acceleration tick
173 // TODO : This works, but PLEASE PLEASE PLEASE if you know a better way to do it, do it better, I don't find this elegant at all, it's just the best I could think of
b6c86164 174 // UPDATE: Yes, this sucks, I have ideas on how to do it better. If this is really bugging you, open a ticket and I'll make it a priority
ded56b35
AW
175
176 int ticks_forward = 3;
177 // We need to take those values here, and then use those instead of the live values, because using the live values inside the loop can break things ( infinite loops etc ... )
178 double next_stepper_rate = stepper->trapezoid_adjusted_rate;
179 double step_events_completed = (double(double(stepper->step_events_completed)/double(1<<16)));
180 double position = ( this->current_position - this->start_position ) * this->direction ;
181 double length = fabs( this->start_position - this->target_position );
182 double last_ratio = -1;
183
184 // Do the startegy above, but if it does not work, look a bit further and try again, and again ...
185 while(1){
186
187 // Find the position where we should be at the next tick
188 double next_ratio = double( step_events_completed + ( next_stepper_rate / 60 / ((double(stepper->acceleration_ticks_per_second)/ticks_forward)) ) ) / double( this->current_block->steps_event_count );
189 double next_relative_position = ( length * next_ratio );
190
191 // Advance
192 // TODO: Proper advance configuration
d9ebc974
AW
193 double advance = double(next_stepper_rate) * ( 0.00001 * 0.15 ) * 0.4 ;
194 //double advance = 0;
ded56b35
AW
195 next_relative_position += ( advance );
196
197 // TODO : all of those "if->return" is very hacky, we should do the math in a way where most of those don't happen, but that requires doing tons of drawing ...
198 if( last_ratio == next_ratio ){ this->acceleration_lock = false; return; }else{ last_ratio = next_ratio; }
199 if( next_ratio == 0 || next_ratio > 1 ){ this->acceleration_lock = false; return; }
200 if( ticks_forward > 1000 ){ this->acceleration_lock = false; return; } // This is very ugly
201
202 // Hack : We have not looked far enough, we compute how far ahead we must look to get a relevant value
203 if( position > next_relative_position ){
204 double far_back = position - next_relative_position;
205 double far_back_ratio = far_back / length;
206 double move_duration = double( this->current_block->steps_event_count ) / ( double(next_stepper_rate) / 60 ) ;
207 double ticks_in_a_move = round( stepper->acceleration_ticks_per_second * move_duration );
208 double ratio_per_tick = 1 / ticks_in_a_move;
209 double ticks_to_equilibrium = ceil(far_back_ratio / ratio_per_tick) + 1;
210 ticks_forward += ticks_to_equilibrium;
211 // Because this is a loop, and we can be interrupted by the stepping interrupt, if that interrupt changes block, the new block may not be solo, and we may get trapped into an infinite loop
212 if( this->mode != FOLLOW ){ this->acceleration_lock = false; return; }
213 continue;
214 }
215
216 // Finally, compute the speed to get to that next position
217 double next_absolute_position = this->start_position + ( this->direction * next_relative_position );
218 double steps_to_next_tick = ( next_relative_position - position ) * this->steps_per_millimeter;
219 double speed_to_next_tick = steps_to_next_tick / ( 1 / double(double(this->kernel->stepper->acceleration_ticks_per_second) / ticks_forward) );
220
221 // Change stepping speed
222 this->set_speed( speed_to_next_tick );
223
224 this->acceleration_lock = false;
13e4a3f9 225 return;
ecc402eb 226 }
0eb11a06 227 }
1a2d88eb 228
ded56b35
AW
229 this->acceleration_lock = false;
230}
1a2d88eb 231
ded56b35
AW
232// Convenience function to set stepping speed
233void Extruder::set_speed( int steps_per_second ){
d9ebc974 234
cd011f58 235 if( steps_per_second < 10 ){ steps_per_second = 10; }
ded56b35
AW
236
237 // TODO : Proper limit config value
238 if( steps_per_second > (this->feed_rate*double(this->steps_per_millimeter))/60 ){
239 steps_per_second = (this->feed_rate*double(this->steps_per_millimeter))/60;
0eb11a06 240 }
1a2d88eb 241
3b1e82d2 242 this->counter_increment = int(floor(double(1<<16)/double(this->kernel->stepper->base_stepping_frequency / steps_per_second)));
d9ebc974 243
4cff3ded
AW
244}
245
246inline void Extruder::stepping_tick(){
81b547a1 247 if( this->paused ){ return; }
4cff3ded 248
3b1e82d2
AW
249 this->step_counter += this->counter_increment;
250 if( this->step_counter > 1<<16 ){
251 this->step_counter -= 1<<16;
4cff3ded 252
3b1e82d2
AW
253 // If we still have steps to do
254 // TODO: Step using the same timer as the robot, and count steps instead of absolute float position
255 if( ( this->current_position < this->target_position && this->direction == 1 ) || ( this->current_position > this->target_position && this->direction == -1 ) ){
256 this->current_position += (double(double(1)/double(this->steps_per_millimeter)))*double(this->direction);
b6c86164 257 this->dir_pin = ((this->direction > 0) ? 1 : 0);
3b1e82d2
AW
258 this->step_pin = 1;
259 }else{
260 // Move finished
261 if( this->mode == SOLO && this->current_block != NULL ){
262 // In follow mode, the robot takes and releases the block, in solo mode we do
263 this->current_block->release();
264 }
265 }
266 }
267}
4cff3ded 268
3b1e82d2
AW
269void Extruder::reset_step_pin(){
270 this->step_pin = 0;
271}