Dear Arduino community!
Thank you for all the information and help you guys put out here for people to learn from. This is my first post but I've been reading a lot of helpful ideas on here.
I have to start saying that I'm a beginner hobbyist at this stuff, both SW/HW side, but admittedly I'm a slightly better coder. Everything I do, I've learned from reading online, so in case you see something crazy in my schematics or code, please know I'm doing my best here.
So, on to the project. I have a vertical drawer that opens and closes with 2 linear actuators and one NEMA17 stepper. One of the actuators is dumb, with limit switches, the other one has no limit switches but potentiometer position feedback. I use an Arduino UNO to control this project. A capacitive toggle switch is used to trigger everything.
The problem; the cap switch toggles ON/OFF by its own. At prototype stage (when I used a breadboard and Dupont cables) I managed to get this problem solved simply by adding a 10k pulldown on the pin used for the switch. Now however, I have moved the Arduino and the drivers to the box and the problem is back - with one difference. Before adding pulldown the switch could toggle at any time and toggle back and forth several times within a minute.
Now however, the random switching only happens when I have started the process of opening or closing the drawer - that is when one or more motors are running. So I'm guessing there is a lot of noise going on in the circuitry.
What am I missing here? There is nothing in the direct vicinity of the switch and it never triggers on its own if no motors are running. I even tried adding a 0,1µF close to the switch with no result.
Other info: Arduino is powered through USB with a 5V source that can handle max 1,5A. The drivers and motors are driven by 12V transformer @ max 3A. The current setting on the TMC2208 is done according to my NEMA17 specs.
#include <elapsedMillis.h>
elapsedMillis timeElapsed;
// Hall Sensors - all arduino pins default to inputs so these don't have to be declared with pinMode
#define sensor_bot A0
#define sensor_mid A1
#define sensor_top A2
#define sensor_back A3
// MD13S
#define P16_dir 7
#define P16_pwm 6 // pwm pin - to MD13S for speed
#define P16_feedback A4 // pwm pin - from P16 - to check position feedback
#define DS300_dir 4
#define DS300_pwm 5
// Beeper
#define beeper 3
// Cap Touch Button
#define touchButtonPin 2
// TMC2208
#define NEMA17_dir 9
#define NEMA17_step 10
#define NEMA17_MS1 11
#define NEMA17_MS2 12
#define NEMA17_EN 13
// Variables
volatile int touch = 0; // indicator for touch - use to trigger start/pause
volatile int pause = 0; // for when button is pressed and no end point is active, break, then reset it
volatile int sensorX = 0; // variable to store X axle sensor value
volatile int sensorY = 0; // variable to store Y axle sensor value
volatile int comingFromPause = 0; // to indicate the shelf is stalled
volatile int pos = 1; // 1 for bot, 2 for top, set false just before calling open/close from end position? Not sure if needed to set false
volatile int firstStart = 1;
volatile int totalSteps = 0; // track steps for backtracking stepper after pause
volatile int P16_pos = 0;
volatile int P16_ignore = 0;
void setup() {
// Touch button pin mode
//pinMode(touchButtonPin, INPUT_PULLUP); // just making sure interrupt pin has a pullup and not floating // used real pulldown with resistor instead
attachInterrupt(digitalPinToInterrupt(touchButtonPin),touchButton,CHANGE); // interupt declaration, whenever there is a change on pin 2
// Beeper pin mode
pinMode(beeper, OUTPUT);
// TMC2208 pin modes and settings
pinMode(NEMA17_dir, OUTPUT);
pinMode(NEMA17_step, OUTPUT);
pinMode(NEMA17_MS1, OUTPUT);
pinMode(NEMA17_MS2, OUTPUT);
pinMode(NEMA17_EN, OUTPUT);
digitalWrite(NEMA17_MS1, HIGH);
digitalWrite(NEMA17_MS2, HIGH);
digitalWrite(NEMA17_EN, HIGH);
// P16 pin modes and settings
pinMode(P16_dir, OUTPUT);
pinMode(P16_pwm, OUTPUT);
pinMode(P16_feedback, INPUT);
digitalWrite(P16_dir, LOW);
digitalWrite(P16_pwm, LOW);
}
void loop() {
if ( touch == 1 ){
// touch = 0; // reset to listen for new touch event as soon as possible - unnecessary at start - done at end
delay(1000);
if ( pos == 1 and comingFromPause == 0 ) {
fStart_Open();
} else if ( pos == 2 and comingFromPause == 0 ) { // use only else if in this function, pause check excluded
fStart_Close();
P16_pos = 0; // reset for opening sequence - needs to be 0 if pause is going to work in fDS300
} else if ( comingFromPause == 1 and pause == 0 ) {
if ( pos == 1 ) { // if started at bottom, return to bottom
fSafe_Close();
} else { // else, started at top, return to top
fSafe_Open();
}
comingFromPause = 0;
}
// last thing to do in void loop under 'if touch'
pause = 0;
touch = 0;
}
}
///////////////////////////////////////////// TOUCH AND STATUS FUNCTIONS /////////////////////////////////////////////
void touchButton() { // try to not have anything else going on here since interrupt can have some unexpected result on things
makeNormalBeep ();
checkStatus();
touch = 1;
}
void checkStatus() {
//check for comingFromPause, endPoints etc - when comingFromPause is true this will practically be bypassed
sensorY = analogRead(sensor_bot);
sensorX = analogRead(sensor_back);
if ( sensorY <= 20 and sensorX <= 100 ) {
pos = 1; // pos can be checked during safe move to make sure reverse move is done in correct direction
firstStart = 0;
return; // if any of these statements are true, we need to leave checkStatus
}
sensorY = analogRead(sensor_top);
P16_pos = analogRead(P16_feedback);
if ( sensorY <= 20 and P16_pos >= 800 ) {
pos = 2;
if ( firstStart == 1 ) {
totalSteps = 350;
}
firstStart = 0;
return;
}
if ( firstStart == 1 ) { // in case of power outage
comingFromPause = 1; // reset at the end of safe move
pause = 0;
totalSteps = 350;
firstStart = 0;
sensorY = analogRead(sensor_top); // if at top but P16 half closed - set pos to 2
if ( sensorY <= 20 ) {
pos = 2;
comingFromPause = 0; // practically, looking for a normal close in this case
}
} else if ( comingFromPause == 0 ) {
comingFromPause = 1; // reset at the end of safe move
pause = 1;
}
}
///////////////////////////////////////////// BEEPER FUNCTIONS /////////////////////////////////////////////
void makeNormalBeep() {
tone(beeper, 3000, 200);
// delays don't work inside an interrupt, this gave me quite a headache until solved! XD
}
void makePauseBeep() {
delay(1500);
tone(beeper, 3000, 150);
delay(200);
tone(beeper, 3000, 150);
}
void makeCautionBeep() {
tone(beeper, 3000, 500);
delay(1000);
tone(beeper, 3000, 500);
delay(1000);
tone(beeper, 3000, 500);
}
void makeErrorBeep() {
tone(beeper, 3000, 500);
delay(1000);
tone(beeper, 3000, 500);
delay(1000);
tone(beeper, 3000, 500);
delay(1000);
tone(beeper, 3000, 500);
delay(1000);
tone(beeper, 3000, 500);
}
///////////////////////////////////////////// NORMAL OPENING FUNCTIONS /////////////////////////////////////////////
void fStart_Open(){ // for opening from starting point
sensorX = analogRead(sensor_back);
if ( sensorX <= 100 ) { // checking for shelf top position before start - making sure it is close to the wall
fStepper_Open(); // void functions are called like this
sensorX = analogRead(sensor_back); // recheck to make sure moved away from wall
if ( pause == 0 and sensorX > 150 ) { // pause check is needed - in case pause has interrupted previous function
fDS300_Open();
} else if ( pause == 1 ) {
//double beep for pause?
return;
} else if ( pause == 0 ) { // in case of failure to move top out
makeErrorBeep();
digitalWrite(NEMA17_EN, HIGH); // unlock NEMA17 so the top can be pulled out manually
comingFromPause = 0;
}
}
}
void fStepper_Open(){
// start by setting correct direction and enable the driver
digitalWrite(NEMA17_EN, LOW);
digitalWrite(NEMA17_dir, HIGH);
totalSteps = 0; // reset step counter before opening
// 4 for loops for movement, 3 last loops for decelaration
for (int i = 1; i <= 275 ; i++) {
if ( pause == 1 ) { // if button pressed during movement - pause becomes TRUE and function returns
makePauseBeep(); // double beep for pause
P16_ignore = 1;
return;
}
else if ( pause == 0 ) {
// these four lines result in 1 step:
digitalWrite(NEMA17_step, HIGH);
delayMicroseconds(1000); // speed, lower means faster
digitalWrite(NEMA17_step, LOW);
delayMicroseconds(1000);
totalSteps = ++totalSteps;
}
}
for (int i = 1; i <= 25 ; i++) { // deceleration
if ( pause == 1 ) {
makePauseBeep(); // double beep for pause
P16_ignore = 1;
return;
}
else if ( pause == 0 ) {
// these four lines result in 1 step:
digitalWrite(NEMA17_step, HIGH);
delayMicroseconds(1500);
digitalWrite(NEMA17_step, LOW);
delayMicroseconds(1500);
totalSteps = ++totalSteps;
}
}
for (int i = 1; i <= 25 ; i++) { // deceleration
if ( pause == 1 ) {
makePauseBeep(); // double beep for pause
P16_ignore = 1;
return;
}
else if ( pause == 0 ) {
// these four lines result in 1 step:
digitalWrite(NEMA17_step, HIGH);
delayMicroseconds(2000);
digitalWrite(NEMA17_step, LOW);
delayMicroseconds(2000);
totalSteps = ++totalSteps;
}
}
for (int i = 1; i <= 25 ; i++) { // deceleration
if ( pause == 1 ) {
makePauseBeep(); // double beep for pause
P16_ignore = 1;
return;
}
else if ( pause == 0 ) {
// these four lines result in 1 step:
digitalWrite(NEMA17_step, HIGH);
delayMicroseconds(4000);
digitalWrite(NEMA17_step, LOW);
delayMicroseconds(4000);
totalSteps = ++totalSteps;
}
}
delay(1000); // small delay before moving on - putting this here will make sure pause beep is triggered if pause activated here
}
void fDS300_Open() {
while ( pause == 0 ) {
//extend DS300
digitalWrite(DS300_dir, HIGH);
digitalWrite(DS300_pwm, HIGH);
//check mid sensor to start opening lid
sensorY = analogRead(sensor_mid);
if ( sensorY <= 20 ) {
fP16_Open();
}
//check top sensor to exit function
sensorY = analogRead(sensor_top);
if ( sensorY <= 20 ) {
delay(1000); //gives time for DS300 to fully open
digitalWrite(DS300_dir, LOW);
digitalWrite(DS300_pwm, LOW);
return;
}
}
P16_pos = analogRead(P16_feedback);
if ( pause == 1 and P16_pos <= 80 ) {
//stop DS300 and double beep if paused before P16 started
digitalWrite(DS300_dir, LOW);
digitalWrite(DS300_pwm, LOW);
P16_ignore = 1;
//double beep for pause
makePauseBeep();
}
}
void fP16_Open() {
int acc_counter = 5;
analogRead(A0); // dummy read to discharge ADC;
P16_pos = analogRead(P16_feedback);
while ( P16_pos < 30 and pause == 0 ) { // Slow open until postion 30, breaks if pause set true
digitalWrite(P16_dir, HIGH);
analogWrite(P16_pwm, 100); // Speed set to 40 - don't go lower - P16 will stall
P16_pos = analogRead(P16_feedback);
}
int prevReading = 0;
do {
while ( acc_counter <= 255 and pause == 0 ) { // Accelerating to top speed, but will break as intended if paused
digitalWrite(P16_dir, HIGH);
analogWrite(P16_pwm, 100+acc_counter);
delay(10);
acc_counter=acc_counter+5;
}
prevReading = P16_pos;
timeElapsed = 0;
while( timeElapsed < 300 ){ delay(1);} // Keep moving until analog reading remains the same for 300ms
P16_pos = analogRead(P16_feedback);
} while ( prevReading != P16_pos and pause == 0 );
digitalWrite(P16_dir, LOW); // turn off P16
digitalWrite(P16_pwm, LOW);
if ( pause == 1 ) {
digitalWrite(DS300_dir, LOW);
digitalWrite(DS300_pwm, LOW);
// Double beep for pause
makePauseBeep();
return;
}
}
///////////////////////////////////////////// NORMAL CLOSING FUNCTIONS /////////////////////////////////////////////
void fStart_Close(){ // for closing from top point
fP16_Close();
if ( pause == 1 ) {
comingFromPause = 1;
return;
}
fDS300_Close();
if ( pause == 1 ) {
comingFromPause = 1;
return;
}
fStepper_Close();
if ( pause == 1 ) {
comingFromPause = 1;
return;
}
}
void fP16_Close () {
P16_pos = analogRead(P16_feedback);
digitalWrite(P16_dir, LOW);
analogWrite(P16_pwm, 200); // speed set to 200
int prevReading = 0;
do {
prevReading = P16_pos;
timeElapsed = 0;
while( timeElapsed < 200 ){ delay(1);} // keep moving until analog reading remains the same for 200ms
P16_pos = analogRead(P16_feedback);
} while ( prevReading != P16_pos and pause == 0 );
digitalWrite(P16_dir, LOW); // turn off P16
digitalWrite(P16_pwm, LOW);
if ( pause == 1) {
makePauseBeep();
return;
} else if ( P16_pos > 100 and pause == 0 ) { // if lid not closed properly, and not because of pause, make error beep and pause
makeErrorBeep();
pause = 1;
comingFromPause = 1;
}
}
void fDS300_Close () {
while ( pause == 0 ) {
//detract DS300
digitalWrite(DS300_dir, LOW);
digitalWrite(DS300_pwm, HIGH);
//check bottom sensor to exit function
sensorY = analogRead(sensor_bot);
if ( sensorY <= 20 ) {
delay(1000); //gives time for DS300 to fully close
digitalWrite(DS300_dir, LOW);
digitalWrite(DS300_pwm, LOW);
return;
}
}
if ( pause == 1 ) {
//stop DS300 and double beep
digitalWrite(DS300_dir, LOW);
digitalWrite(DS300_pwm, LOW);
//double beep for pause
makePauseBeep();
}
}
void fStepper_Close () {
digitalWrite(NEMA17_EN, LOW);
digitalWrite(NEMA17_dir, LOW);
for (int i = 0; i <= 350 ; i++) {
if ( pause == 1 ) { // if button pressed during movement - pause becomes TRUE and function returns
makePauseBeep(); // double beep for pause
return;
}
// these four lines result in 1 step:
digitalWrite(NEMA17_step, HIGH);
delayMicroseconds(2000); // speed, lower means faster
digitalWrite(NEMA17_step, LOW);
delayMicroseconds(2000);
totalSteps = --totalSteps;
}
}
///////////////////////////////////////////// SAFE MODE OPENING FUNCTION /////////////////////////////////////////////
void fSafe_Open() { //safe mode cannot be interrupted - done by choice
makeCautionBeep();
delay(2000);
//safe open stepper
digitalWrite(NEMA17_EN, LOW);
digitalWrite(NEMA17_dir, HIGH);
totalSteps = 350 - totalSteps; // how many steps to open? if fully open this will result in 0 and skip stepper
for (int i = 0; i <= totalSteps ; i++) {
// these four lines result in 1 step:
digitalWrite(NEMA17_step, HIGH);
delayMicroseconds(2000); // speed, lower means faster
digitalWrite(NEMA17_step, LOW);
delayMicroseconds(2000);
}
//safe open DS300 - delay before continue
sensorY = analogRead(sensor_top);
while ( sensorY >= 20 ) {
digitalWrite(DS300_dir, HIGH);
digitalWrite(DS300_pwm, HIGH);
sensorY = analogRead(sensor_top);
}
delay(1000); //gives time for DS300 to open fully
digitalWrite(DS300_dir, LOW);
digitalWrite(DS300_pwm, LOW);
//safe open lid and stop P16 - delay before continue
analogRead(A0); // dummy read to discharge ADC;
P16_pos = analogRead(P16_feedback);
while ( P16_pos < 30 ) { // Slow open until postion 30, breaks if pause set true
digitalWrite(P16_dir, HIGH);
analogWrite(P16_pwm, 40); // Speed set to 40 - don't go lower - P16 will stall
P16_pos = analogRead(P16_feedback);
}
int prevReading = 0;
do {
digitalWrite(P16_dir, HIGH);
analogWrite(P16_pwm, 200);
delay(10);
prevReading = P16_pos;
timeElapsed = 0;
while( timeElapsed < 300 ){ delay(1);} // Keep moving until analog reading remains the same for 300ms
P16_pos = analogRead(P16_feedback);
} while ( prevReading != P16_pos );
digitalWrite(P16_dir, LOW); // turn off P16
digitalWrite(P16_pwm, LOW);
comingFromPause = 0;
totalSteps = 350;
}
///////////////////////////////////////////// SAFE MODE CLOSING FUNCTION /////////////////////////////////////////////
void fSafe_Close() { //safe mode cannot be interrupted - done by choice
makeCautionBeep();
delay(2000);
//safe close lid and stop P16 - delay before continue
if ( P16_ignore == 0 ) {
P16_pos = analogRead(P16_feedback);
digitalWrite(P16_dir, LOW);
analogWrite(P16_pwm, 100); // Speed set to 100
int prevReading = 0;
do {
prevReading = P16_pos;
timeElapsed = 0;
while( timeElapsed < 200 ){ delay(1);} // Keep moving until analog reading remains the same for 200ms
P16_pos = analogRead(P16_feedback);
} while ( prevReading != P16_pos );
digitalWrite(P16_dir, LOW); // turn off P16
digitalWrite(P16_pwm, LOW);
delay(1000);
}
//safe close DS300 - delay before continue
sensorY = analogRead(sensor_bot);
while ( sensorY >= 20 ) {
digitalWrite(DS300_dir, LOW);
digitalWrite(DS300_pwm, HIGH);
sensorY = analogRead(sensor_bot);
}
delay(1000); //gives time for DS300 to close properly
digitalWrite(DS300_dir, LOW);
digitalWrite(DS300_pwm, LOW);
//safe close stepper
digitalWrite(NEMA17_EN, LOW);
digitalWrite(NEMA17_dir, LOW);
for (int i = 0; i <= totalSteps ; i++) {
// these four lines result in 1 step:
digitalWrite(NEMA17_step, HIGH);
delayMicroseconds(2000); // speed, lower means faster
digitalWrite(NEMA17_step, LOW);
delayMicroseconds(2000);
}
comingFromPause = 0;
P16_ignore = 0;
}
Happy for any help! Let me know if I've missed any more info you would need.
/Z