3 Handles a filament detector that has an optical encoder wheel, that generates pulses as the filament
5 It also supports a "bulge" detector that triggers if the filament has a bulge in it
8 #include "FilamentDetector.h"
11 #include "checksumm.h"
12 #include "ConfigValue.h"
13 #include "SlowTicker.h"
14 #include "PublicData.h"
15 #include "StreamOutputPool.h"
16 #include "StreamOutput.h"
17 #include "SerialMessage.h"
18 #include "FilamentDetector.h"
22 #include "InterruptIn.h" // mbed
23 #include "us_ticker_api.h" // mbed
25 #define extruder_checksum CHECKSUM("extruder")
27 #define filament_detector_checksum CHECKSUM("filament_detector")
28 #define enable_checksum CHECKSUM("enable")
29 #define encoder_pin_checksum CHECKSUM("encoder_pin")
30 #define bulge_pin_checksum CHECKSUM("bulge_pin")
31 #define seconds_per_check_checksum CHECKSUM("seconds_per_check")
32 #define pulses_per_mm_checksum CHECKSUM("pulses_per_mm")
34 FilamentDetector::FilamentDetector()
37 filament_out_alarm
= false;
42 FilamentDetector::~FilamentDetector()
44 if(encoder_pin
!= nullptr) delete encoder_pin
;
47 void FilamentDetector::on_module_loaded()
49 // if the module is disabled -> do nothing
50 if(!THEKERNEL
->config
->value( filament_detector_checksum
, enable_checksum
)->by_default(false)->as_bool()) {
51 // as this module is not needed free up the resource
56 // encoder pin has to be interrupt enabled pin like 0.26, 0.27, 0.28
58 dummy_pin
.from_string( THEKERNEL
->config
->value(filament_detector_checksum
, encoder_pin_checksum
)->by_default("nc" )->as_string());
59 this->encoder_pin
= dummy_pin
.interrupt_pin();
60 if(this->encoder_pin
== nullptr) {
61 // was not a valid interrupt pin
66 // set interrupt on rising edge
67 this->encoder_pin
->rise(this, &FilamentDetector::on_pin_rise
);
68 NVIC_SetPriority(EINT3_IRQn
, 16); // set to low priority
70 // optional bulge detector
71 bulge_pin
.from_string( THEKERNEL
->config
->value(filament_detector_checksum
, bulge_pin_checksum
)->by_default("nc" )->as_string())->as_input();
72 if(bulge_pin
.connected()) {
74 THEKERNEL
->slow_ticker
->attach( 100, this, &FilamentDetector::button_tick
);
77 // how many seconds between checks, must be long enough for several pulses to be detected, but not too long
78 seconds_per_check
= THEKERNEL
->config
->value(filament_detector_checksum
, seconds_per_check_checksum
)->by_default(2)->as_number();
80 // the number of pulses per mm of filament moving through the detector, can be fractional
81 pulses_per_mm
= THEKERNEL
->config
->value(filament_detector_checksum
, pulses_per_mm_checksum
)->by_default(1)->as_number();
83 // register event-handlers
84 register_for_event(ON_SECOND_TICK
);
85 register_for_event(ON_MAIN_LOOP
);
86 register_for_event(ON_CONSOLE_LINE_RECEIVED
);
87 this->register_for_event(ON_GCODE_RECEIVED
);
90 void FilamentDetector::send_command(std::string msg
, StreamOutput
*stream
)
92 struct SerialMessage message
;
93 message
.message
= msg
;
94 message
.stream
= stream
;
95 THEKERNEL
->call_event(ON_CONSOLE_LINE_RECEIVED
, &message
);
98 // needed to detect when we resume
99 void FilamentDetector::on_console_line_received( void *argument
)
101 if(!suspended
) return;
103 SerialMessage new_message
= *static_cast<SerialMessage
*>(argument
);
104 string possible_command
= new_message
.message
;
105 string cmd
= shift_parameter(possible_command
);
106 if(cmd
== "resume") {
111 void FilamentDetector::on_gcode_received(void *argument
)
113 Gcode
*gcode
= static_cast<Gcode
*>(argument
);
115 if (gcode
->m
== 405) { // diable filament detector
118 }else if (gcode
->m
== 406) { // enable filament detector
123 }else if (gcode
->m
== 407) { // display filament detector pulses and status
125 if(!PublicData::get_value( extruder_checksum
, (void **)&rd
)) {
126 float e_moved
= *(rd
+5); // current position for extruder in mm
127 float delta
= e_moved
- e_last_moved
;
128 gcode
->stream
->printf("Extruder moved: %f mm\n", delta
);
131 gcode
->stream
->printf("Encoder pulses: %u\n", pulses
.load());
132 if(this->suspended
) gcode
->stream
->printf("Filament detector triggered\n");
133 gcode
->stream
->printf("Filament detector is %s\n", active
?"enabled":"disabled");
138 void FilamentDetector::on_main_loop(void *argument
)
140 if (active
&& this->filament_out_alarm
) {
141 this->filament_out_alarm
= false;
142 THEKERNEL
->streams
->printf("// Filament Detector has detected a filament jam\n");
143 this->suspended
= true;
144 // fire suspend command
145 this->send_command( "M600", &(StreamOutput::NullStream
) );
149 void FilamentDetector::on_second_tick(void *argument
)
151 if(++seconds_passed
>= seconds_per_check
) {
157 // encoder pin interrupt
158 void FilamentDetector::on_pin_rise()
163 void FilamentDetector::check_encoder()
165 uint32_t pulse_cnt
= this->pulses
.exchange(0); // atomic load and reset
166 if(suspended
) return; // already suspended
168 // get number of E steps taken and make sure we have seen enough pulses to cover that
170 if(!PublicData::get_value( extruder_checksum
, (void **)&rd
)) return;
171 float e_moved
= *(rd
+5); // current position for extruder in mm
172 if(isnan(e_last_moved
)) {
173 e_last_moved
= e_moved
;
177 float delta
= e_moved
- e_last_moved
;
178 e_last_moved
= e_moved
;
180 // figure out how many pulses need to have happened to cover that e move
181 uint32_t needed_pulses
= floorf(delta
*pulses_per_mm
);
182 // NOTE if needed_pulses is 0 then extruder did not move since last check, or not enough to register
183 if(needed_pulses
== 0) return;
186 // we got no pulses and E moved since last time so fire off alarm
187 this->filament_out_alarm
= true;
191 uint32_t FilamentDetector::button_tick(uint32_t dummy
)
193 if(!bulge_pin
.connected()) return 0;
195 if(bulge_pin
.get()) {
196 // we got a trigger from the bulge detector
197 this->filament_out_alarm
= true;