-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathFeeder.cpp
452 lines (369 loc) · 13.2 KB
/
Feeder.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
#include "Feeder.h"
#include "config.h"
bool FeederClass::isInitialized() {
if(this->feederNo == -1)
return false;
else
return true;
}
void FeederClass::initialize(uint8_t _feederNo) {
this->feederNo = _feederNo;
}
#ifdef HAS_FEEDBACKLINES
bool FeederClass::hasFeedbackLine() {
if(feederFeedbackPinMap[this->feederNo] != -1) {
return true;
} else {
return false;
}
}
#endif
void FeederClass::outputCurrentSettings() {
Serial.print("M");
Serial.print(MCODE_UPDATE_FEEDER_CONFIG);
Serial.print(" N");
Serial.print(this->feederNo);
Serial.print(" A");
Serial.print(this->feederSettings.full_advanced_angle);
Serial.print(" B");
Serial.print(this->feederSettings.half_advanced_angle);
Serial.print(" C");
Serial.print(this->feederSettings.retract_angle);
Serial.print(" F");
Serial.print(this->feederSettings.feed_length);
Serial.print(" U");
Serial.print(this->feederSettings.time_to_settle);
Serial.print(" V");
Serial.print(this->feederSettings.motor_min_pulsewidth);
Serial.print(" W");
Serial.print(this->feederSettings.motor_max_pulsewidth);
#ifdef HAS_FEEDBACKLINES
Serial.print(" X");
Serial.print(this->feederSettings.ignore_feedback);
#endif
Serial.println();
}
void FeederClass::setup() {
//load settings from eeprom
this->loadFeederSettings();
//attach servo to pin, after settings are loaded
this->servo.attach(feederPinMap[this->feederNo],this->feederSettings.motor_min_pulsewidth,this->feederSettings.motor_max_pulsewidth);
//feedback input
//microswitch is active low (NO connected to feedback-pin)
#ifdef HAS_FEEDBACKLINES
if(this->hasFeedbackLine())
pinMode((uint8_t)feederFeedbackPinMap[this->feederNo],INPUT_PULLUP);
this->lastButtonState=digitalRead(feederFeedbackPinMap[this->feederNo]);
#endif
//put on defined position
this->gotoRetractPosition();
}
FeederClass::sFeederSettings FeederClass::getSettings() {
return this->feederSettings;
}
void FeederClass::setSettings(sFeederSettings UpdatedFeederSettings) {
this->feederSettings=UpdatedFeederSettings;
#ifdef DEBUG
Serial.println(F("updated feeder settings"));
this->outputCurrentSettings();
#endif
}
void FeederClass::loadFeederSettings() {
uint16_t adressOfFeederSettingsInEEPROM = EEPROM_FEEDER_SETTINGS_ADDRESS_OFFSET + this->feederNo * sizeof(this->feederSettings);
EEPROM.readBlock(adressOfFeederSettingsInEEPROM, this->feederSettings);
#ifdef DEBUG
Serial.println(F("loaded settings from eeprom:"));
this->outputCurrentSettings();
#endif
}
void FeederClass::saveFeederSettings() {
uint16_t adressOfFeederSettingsInEEPROM = EEPROM_FEEDER_SETTINGS_ADDRESS_OFFSET + this->feederNo * sizeof(this->feederSettings);
EEPROM.writeBlock(adressOfFeederSettingsInEEPROM, this->feederSettings);
#ifdef DEBUG
Serial.println(F("stored settings to eeprom:"));
this->outputCurrentSettings();
#endif
}
void FeederClass::factoryReset() {
//just save the defaults to eeprom...
this->saveFeederSettings();
}
void FeederClass::gotoPostPickPosition() {
if(this->feederPosition==sAT_FULL_ADVANCED_POSITION) {
this->gotoRetractPosition();
#ifdef DEBUG
Serial.println("gotoPostPickPosition retracted feeder");
#endif
} else {
#ifdef DEBUG
Serial.println("gotoPostPickPosition didn't need to retract feeder");
#endif
}
}
void FeederClass::gotoRetractPosition() {
this->servo.write(this->feederSettings.retract_angle);
this->feederPosition=sAT_RETRACT_POSITION;
this->feederState=sMOVING;
#ifdef DEBUG
Serial.println("going to retract now");
#endif
}
void FeederClass::gotoHalfAdvancedPosition() {
this->servo.write(this->feederSettings.half_advanced_angle);
this->feederPosition=sAT_HALF_ADVANCED_POSITION;
this->feederState=sMOVING;
#ifdef DEBUG
Serial.println("going to half adv now");
#endif
}
void FeederClass::gotoFullAdvancedPosition() {
this->servo.write(this->feederSettings.full_advanced_angle);
this->feederPosition=sAT_FULL_ADVANCED_POSITION;
this->feederState=sMOVING;
#ifdef DEBUG
Serial.println("going to full adv now");
#endif
}
void FeederClass::gotoAngle(uint8_t angle) {
this->servo.write(angle);
#ifdef DEBUG
Serial.print("going to ");
Serial.print(angle);
Serial.println("deg");
#endif
}
bool FeederClass::advance(uint8_t feedLength, bool overrideError = false) {
#ifdef DEBUG
Serial.println(F("advance triggered"));
Serial.println(this->reportFeederErrorState());
#endif
#ifdef DEBUG
Serial.print(F("feederIsOk: "));
Serial.println(this->feederIsOk());
Serial.print(F("overrideError: "));
Serial.println(overrideError);
#endif
//check whether feeder is OK before every advance command
if( !this->feederIsOk() ) {
//feeder is in error state, usually this would lead to exit advance with false and no advancing cycle started
if(!overrideError) {
//return with false means an error, that is not ignored/overridden
//error, and error was not overridden -> return false, advance not successful
return false;
} else {
#ifdef DEBUG
Serial.println(F("overridden error temporarily"));
#endif
}
}
//check, what to do? if not, return quickly
if(feedLength==0) {
//nothing to do, just return
#ifdef DEBUG
Serial.println(F("advance ignored, 0 feedlength was given"));
#endif
} else if ( feedLength>0 && this->feederState!=sIDLE ) {
//last advancing not completed! ignore newly received command
//TODO: one could use a queue
#ifdef DEBUG
Serial.print(F("advance ignored, feedlength>0 given, but feederState!=sIDLE"));
Serial.print(F(" (feederState="));
Serial.print(this->feederState);
Serial.println(F(")"));
#endif
} else {
//OK, start new advance-proc
//feed multiples of 2 possible: 2/4/6/8/10/12,...
#ifdef DEBUG
Serial.print(F("advance initialized, remainingFeedLength="));
Serial.println(feedLength);
#endif
this->remainingFeedLength=feedLength;
}
//return true: advance started okay
return true;
}
bool FeederClass::feederIsOk() {
if(this->getFeederErrorState() == sERROR) {
return false;
} else {
return true;
}
}
FeederClass::tFeederErrorState FeederClass::getFeederErrorState() {
#ifdef HAS_FEEDBACKLINES
if(!this->hasFeedbackLine()) {
//no feedback-line, return always OK
//no feedback pin defined or feedback shall be ignored
return sOK_NOFEEDBACKLINE;
}
if( digitalRead((uint8_t)feederFeedbackPinMap[this->feederNo]) == LOW ) {
//the microswitch pulls feedback-pin LOW if tension of cover tape is OK. motor to pull tape is off then
//no error
return sOK;
} else {
//microswitch is not pushed down, this is considered as an error
if(this->feederSettings.ignore_feedback==1) {
//error present, but ignore
return sERROR_IGNORED;
} else {
//error present, report fail
return sERROR;
}
}
#else
return sOK_NOFEEDBACKLINE;
#endif
}
String FeederClass::reportFeederErrorState() {
switch(this->getFeederErrorState()) {
case sOK_NOFEEDBACKLINE:
return "getFeederErrorState: sOK_NOFEEDBACKLINE (no feedback line for feeder, impliciting feeder OK)";
break;
case sOK:
return "getFeederErrorState: sOK (feedbackline checked, explicit feeder OK)";
break;
case sERROR_IGNORED:
return "getFeederErrorState: sERROR_IGNORED (error, but ignored per feeder setting X1)";
break;
case sERROR:
return "getFeederErrorState: sERROR (error signaled on feedbackline)";
break;
default:
return "illegal state in reportFeederErrorState";
}
}
//called when M-Code to enable feeder is issued
void FeederClass::enable() {
this->feederState=sIDLE;
}
//called when M-Code to disable feeder is issued
void FeederClass::disable() {
this->feederState=sDISABLED;
}
void FeederClass::update() {
#ifdef HAS_FEEDBACKLINES
//routine for detecting manual feed via tensioner microswitch.
//useful for setup a feeder. press tensioner short to advance by feeder's default feed length
//feeder have to be enabled for this, otherwise this feature doesn't work and pressing the tensioner can't be detected due to open mosfet on controller pcb.
if(this->feederState==sIDLE) { //only check feedback line if feeder is idle. this shall not interfere with the feedbackline-checking to detect the error state of the feeder
if (millis() - this->lastTimeFeedbacklineCheck >= 10UL) { //to debounce, check every 10ms the feedbackline.
this->lastTimeFeedbacklineCheck=millis(); //update last time checked
int buttonState = digitalRead(feederFeedbackPinMap[this->feederNo]); //read level of feedbackline (active low)
if ( this->feedbackLineTickCounter > 0 ) { //to debounce there is a timer
this->feedbackLineTickCounter++;
}
if ( (buttonState != this->lastButtonState) && (buttonState == LOW)) { //event: button state changed to low (tensioner pressed)
this->lastButtonState=buttonState; //update state
this->feedbackLineTickCounter=1; //start counter
#ifdef DEBUG
Serial.println(F("buttonState changed to low"));
#endif
} else if (buttonState != this->lastButtonState) {
this->lastButtonState=buttonState; //update in case button went high again
}
if ( (this->feedbackLineTickCounter > 5) ) {
//after 50ms the microswitch is expected to be debounced, so a potential manual feed can be issued, when going to high level again.
if(buttonState==HIGH) {
//button released, we have a valid feed command now
#ifdef DEBUG
Serial.print(F("Manual feed triggered for feeder N"));
Serial.print(this->feederNo);
Serial.print(F(", advancing feeders default length "));
Serial.print(this->feederSettings.feed_length);
Serial.println(F("mm."));
#endif
//trigger feed with default feeder length, errors are overridden.
this->advance(this->feederSettings.feed_length,true);
//reset
this->feedbackLineTickCounter=0;
}
//check for invalidity
if (this->feedbackLineTickCounter > 50) { //button pressed too long (this is the case, too, if the cover tape was inserted and properly tensioned)
#ifdef DEBUG
Serial.println(F("Potential manual feed rejected (button pressed too long, probably cover tape was inserted properly)"));
#endif
//reset counter to reject potential feed
this->feedbackLineTickCounter=0;
}
}
}
} else {
//permanently reset vars to don't do anything if not idle...
this->lastButtonState = digitalRead(feederFeedbackPinMap[this->feederNo]); //read level of feedbackline (active low)
feedbackLineTickCounter=0;
}
#endif
//state machine-update-stuff (for settle time)
if(this->lastFeederPosition!=this->feederPosition) {
this->lastTimePositionChange=millis();
this->lastFeederPosition=this->feederPosition;
}
//time to change the position?
if (millis() - this->lastTimePositionChange >= (unsigned long)this->feederSettings.time_to_settle) {
//now servo is expected to have settled at its designated position, so do some stuff
if(this->feederState==sADVANCING_CYCLE_COMPLETED) {
Serial.println("ok, advancing cycle completed");
this->feederState=sIDLE;
}
//if no need for feeding exit fast.
if(this->remainingFeedLength==0) {
if(this->feederState!=sDISABLED)
//if feeder are not disabled:
//make sure sIDLE is entered always again (needed if gotoXXXPosition functions are called directly instead by advance() which would set a remainingFeedLength)
this->feederState=sIDLE;
return;
} else {
this->feederState=sMOVING;
}
#ifdef DEBUG
Serial.print("remainingFeedLength before working: ");
Serial.println(this->remainingFeedLength);
#endif
switch (this->feederPosition) {
/* ------------------------------------- RETRACT POS ---------------------- */
case sAT_RETRACT_POSITION: {
if(this->remainingFeedLength>=FEEDER_MECHANICAL_ADVANCE_LENGTH) {
//goto full advance-pos
this->gotoFullAdvancedPosition();
this->remainingFeedLength-=FEEDER_MECHANICAL_ADVANCE_LENGTH;
} else if(this->remainingFeedLength>=FEEDER_MECHANICAL_ADVANCE_LENGTH/2) {
//goto half advance-pos
this->gotoHalfAdvancedPosition();
this->remainingFeedLength-=FEEDER_MECHANICAL_ADVANCE_LENGTH/2;
}
}
break;
/* ------------------------------------- HALF-ADVANCED POS ---------------------- */
case sAT_HALF_ADVANCED_POSITION: {
if(this->remainingFeedLength>=FEEDER_MECHANICAL_ADVANCE_LENGTH/2) {
//goto full advance-pos
this->gotoFullAdvancedPosition();
this->remainingFeedLength-=FEEDER_MECHANICAL_ADVANCE_LENGTH/2;
}
}
break;
/* ------------------------------------- FULL-ADVANCED POS ---------------------- */
case sAT_FULL_ADVANCED_POSITION: {
// if coming here and remainingFeedLength==0, then the function is aborted above already, thus no retract after pick
// if coming here and remainingFeedLength >0, then the feeder goes to retract for next advance move
this->gotoRetractPosition();
}
break;
default: {
//state not relevant for advancing...
//return error, should not occur?
}
break;
}
#ifdef DEBUG
Serial.print("remainingFeedLength after working: ");
Serial.println(this->remainingFeedLength);
#endif
//just finished advancing? set flag to send ok in next run after settle-time to let the pnp go on
if(this->remainingFeedLength==0) {
this->feederState=sADVANCING_CYCLE_COMPLETED;
}
}
return;
}