Servo with delay at zero cross [SOLVED]

I am trying to run this code all compiles correctly but upon button press nothing happens

#include <TimerOne.h>//used for simulation trigger of zero crossing interupt
#include <Servo.h>

volatile boolean zeroCrossingFlag = false;//set in zero crossing interrupt
Servo myServo;
//button variables push button on pin 4
byte prevVal6 = 0;
byte val6 = 0;
int i = 0;
int btn = 6;
int pulse = 7;
int zc = 2;
int LED = 13;

void setup() {
  myServo.attach(5);
  myServo.write(7);
  Serial.begin(9600);
  pinMode(pulse, OUTPUT);//jump pin 7 to pin 2 for INT0
  Timer1.initialize(10000);//fire pulse every 10ms 50Hz
  Timer1.attachInterrupt(ZCpulse);//timer interrupt called
  pinMode(btn, INPUT_PULLUP);//button pin
  pinMode(zc, INPUT_PULLUP);//zero cross interrupt pin
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  attachInterrupt(0, setFlag, FALLING);//zero cross detect
}

void loop() 
{
  int sensorVal = digitalRead(zc);
  Serial.println(sensorVal);
  val6 = digitalRead(btn);//poll button every loop

  //button press to trigger weld cycle
  if (val6 == LOW && prevVal6 == HIGH) //if state change
  {
    delay(10);//blocking debounce
    val6 = digitalRead(btn);//read again

    if (val6 == LOW && prevVal6 == HIGH)//steady reading?
    {
      zeroCrossingFlag = false;//set flag false and wait for next
      delay(5);//delay to trigger on sine wave peak
      digitalWrite(LED, HIGH);
      myServo.write(180);
      delay(50);//preweld time
      digitalWrite(LED, LOW);
      zeroCrossingFlag = false;//set flag false and wait for next
      while (!zeroCrossingFlag) {};
      delay(5);//delay to trigger on sine wave peak
      digitalWrite(LED, HIGH);
      delay(450);//weld step
      myServo.write(6);
      digitalWrite(LED, LOW);
    }
  }
  prevVal6 = val6;
}

void ZCpulse()
{
  digitalWrite(pulse, HIGH);
  delayMicroseconds(10);//10 us every 10 milliseconds
  digitalWrite(pulse, LOW);
}

void setFlag()
{
  zeroCrossingFlag = true;//interrupt sets flag true

}

Here am trying turn servo to 90 degree and then back to 0 after some time.

But this works well,

#include <Servo.h>

Servo myServo;
const int btn = 6;
int btnState = 0;
int i = 0;

void setup()
{
  pinMode(btn, INPUT_PULLUP);
  myServo.attach(5);
  myServo.write(6);
}

void loop()
{
    btnState = digitalRead(btn);
    delay(10);
    if (btnState == LOW)
    {
      for (i = 6; i <= 90; i += 1)
      {
        myServo.write(i);
        delay(15);
      }
      delay(1000);
      for (i = 90; i >= 6; i -= 2)
      {
        myServo.write(i);
        delay(15);
      }
      delay(100);
      myServo.write(0);
    }
  }

So why not use the working program?

Or if you need extra functionality build upon the working program adding pieces one step at a time and testing at every step so you can identify what causes a problem.

It could be that the TimerOne library is using the same Hardware Timer as the Servo library. Try the ServoTimer2 library.

...R

So why not use the working program?

i did try that way but it does not seem to be straight forward. will try again.

Try the ServoTimer2 library.

Does it take Timer1.h by default ? now to use the timer2.h do i just include it only or is there something else that needs to be done. The previous did i did not had to include the timer2.h

Read attachInterrupt() - Arduino Reference

I guess you are attaching the interrupt incorrectly.

i doubt if it would be the interupt as it was working on my existing project. how did you guess it ?

anishkgt:
i doubt if it would be the interupt as it was working on my existing project. how did you guess it ?

According to the linked documentation you are using not recommended way to call the function. You should not use just a number as the interrupt source. I do not know what Arduino you are using but if it is UNO you should only use pin 2 or 3 for the interrupt the documentation says.

So I guessed your code is endlessly waiting for the interrupt to come.

anishkgt:

Try the ServoTimer2 library.

Does it take Timer1.h by default ? now to use the timer2.h do i just include it only or is there something else that needs to be done. The previous did i did not had to include the timer2.h

That seems very illogical to me.

If the problem is that your TimerOne library and your Servo library clash then why, if you were changing to the ServoTimer2 library, would you also think of changing your other library to use Timer2 and create a clash on a different timer.

In any case, in the time since you wrote Reply #2 you could have tried the ServoTimer2 library and found out if it solved the problem.

...R

Smajdalf:
According to the linked documentation you are using not recommended way to call the function. You should not use just a number as the interrupt source. I do not know what Arduino you are using but if it is UNO you should only use pin 2 or 3 for the interrupt the documentation says.

So I guessed your code is endlessly waiting for the interrupt to come.

I use the UNO and the interrupt is on Pin 2

int zc = 2;

the first code shows it scrolled to the top.

In any case, in the time since you wrote Reply #2 you could have tried the ServoTimer2 library and found out if it solved the problem.

I wasn't sure if the servo took the Timer1.h as the default, will try replacing the timer1 with timer2 and see how it goes.

I think problem is on this line:

attachInterrupt(0, setFlag, FALLING);//zero cross detect

You are attaching ISR named setFlag to interrupt "0". I have no idea how Arduino IDE interprets it. If it interprets it as "Interrupt on falling edge of pin 0" you are in problem - only pins 2 and 3 are supported. If it interprets it as "interrupt number 0" it is another problem: that should be interrupt triggered by reset. What about trying

attachInterrupt(digitalPinToInterrupt(zc), setFlag, FALLING);//zero cross detect

as the linked page suggests?

I think problem is on this line:

attachInterrupt(0, setFlag, FALLING);//zero cross detect

You are attaching ISR named setFlag to interrupt "0". I have no idea how Arduino IDE interprets it.

You have no idea how the function interprets the first argument, but you think that the code is wrong. Maybe you should actually look at the documentation before making statements like that.

The first argument is the interrupt number. The digitalPinToInterrupt() macro was added later, for people too lazy to read the documentation to determine the interrupt number that corresponded to a particular pin.

Smajdalf:
I have no idea how Arduino IDE interprets it

Interrupt 0 is pin 2 on an Uno.

...R
PS @PaulS has probably said it better

anishkgt:
i did try that way but it does not seem to be straight forward. will try again.

That is not a sufficient explanation to enable me to help. And, in fact, your Original Post has almost no explanation of what you are trying to achieve.

Take a few minutes to write a decent explanation of your project.

...R

PaulS:
You have no idea how the function interprets the first argument, but you think that the code is wrong. Maybe you should actually look at the documentation before making statements like that.

The first argument is the interrupt number. The digitalPinToInterrupt() macro was added later, for people too lazy to read the documentation to determine the interrupt number that corresponded to a particular pin.

I must admit I have no idea where proper documentation for such advanced functions is. Is it easier for me tl read the datasheet and use the function directly. But I have read the above linked "documentation" and it stated it should not be used in the way the OP used it with no clear explanation how it would work in his case. (How it knows if the first argument is pin number or interrupt number?). But still I think if the interrupt is working it is close to blind luck. It looks like OP believes
int zc = 2;
defines pin 2 as the interrupt source. But it is not true and changing it to 3 will not change the interrupt source to pin 3.

But there is possibly another problem. OP uses
delayMicroseconds(10);
inside ISR. If delayMicroseconds rely on interrupts (as some timing functions do) it will freeze the Arduino first time the ISR is called!

Smajdalf:
I must admit I have no idea where proper documentation for such advanced functions is. Is it easier for me tl read the datasheet and use the function directly.

That is perfectly reasonable for your own programming.

But please don't jump into someone else's problem when you are out of your depth.

It is certainly not working due to "blind luck".

...R

@Smajdalf states

But there is possibly another problem. OP uses
delayMicroseconds(10);
inside ISR. If delayMicroseconds rely on interrupts (as some timing functions do) it will freeze the Arduino first time the ISR is called!

This is incorrect. delayMicroseconds uses empty processor cycles to create delays. It does not require interrupts.

This is now the second time in this thread that you have incorrectly pointed at a non-problem. You are confusing the OP more than helping. When providing assistance on this board, you have the responsibility to check out what you say.

Robin2 has correctly pointed out the conflict between TimerOne.h and Servo.h in that they are both using hardware Timer1. Using ServoTimer2.h would appear to be the obvious solution.

Smajdalf:
But there is possibly another problem. OP uses
delayMicroseconds(10);
inside ISR. If delayMicroseconds rely on interrupts (as some timing functions do) it will freeze the Arduino first time the ISR is called!

I missed this earlier.

The answer to that is very much simpler. One should NEVER have a delay inside an ISR. An ISR should be written so it completes as fast as possible and lets the Arduino go back to regular stuff.

...R

One should NEVER have a delay inside an ISR

There is a big difference between delay() and delayMicroseconds(). Using delayMicroseconds() to generate short trigger pulses from an ISR seems very appropriate.

cattledog:
@Smajdalf states
This is incorrect. delayMicroseconds uses empty processor cycles to create delays. It does not require interrupts.

This is now the second time in this thread that you have incorrectly pointed at a non-problem. You are confusing the OP more than helping. When providing assistance on this board, you have the responsibility to check out what you say.

Robin2 has correctly pointed out the conflict between TimerOne.h and Servo.h in that they are both using hardware Timer1. Using ServoTimer2.h would appear to be the obvious solution.

I doubt it is a non-problem. Using delays inside ISR is bad practice anyway. I wonder how many people know delayMicroseconds inside ISR is safe but delay is not.

Anyway the rest of your post is true - I completly missed the part where Robin2 explains he suggests change of libraries because of Timer1 conflict. I will take more care while reading other posts.

Ah cattledog, You Rock :slight_smile:

This is now the second time in this thread that you have incorrectly pointed at a non-problem. You are confusing the OP more than helping.

Actually yea, i was quite confused but i knew our friend here @Smajdalf was wrong but i did not correct it saying " it is wrong that interupt 0 is actually pin on the UNO" as mentioned by@Robin2. Since i, myself did not understand it very well.i did not intervene moreover this bit of the code was from cattleDog himself and till today this works for me on my arduino spot welder.

Now back to the thread. The use of the servo is to lower and raise the electrode for my previous project. Basically my intention is to activate the servo to lower the electrodes and wait for zero cross then fire the weld then activate servo to retract electrode.

So the code here is a bit more refined than the previous but still nothing happens tho…still figuring it out

#include <Servo.h>
#include <ServoTimer2.h>
#include <TimerOne.h>
Servo myServo;

volatile boolean zeroCrossingFlag = false;
byte zCd = 2;  // Zero Crossing Detect
const byte servo = 5;
const byte btn = 6 ;
const byte LED = 4;
int pulse = 7; //Simulate zero crossing pulse, jump pin 7 to 2
const int tgr_dly = 4245; //delay after zero cross at peak sine - average of 50/60Hz
const int debounce = 10; // ms debounce period to prevent flickering when pressing or releasing the button
const int holdTime = 800; // ms hold period: how long to wait for press+hold event
int btnState = 0; // value read from button
int buttonLast = HIGH; // buffered value of the button's previous state
long btnDnTime; // time the button was pressed down
long btnUpTime; // time the button was released
boolean ignoreUp = false; // whether to ignore the button release because the click+hold was triggered

void setup()
{
  Timer1.initialize(10000);//fire pulse every 10ms 50Hz
  Timer1.attachInterrupt(ZCpulse);//timer interrupt called
  myServo.attach(servo);
  myServo.write(6);
  pinMode(servo, OUTPUT);
  pinMode(pulse, OUTPUT);
  pinMode(LED, OUTPUT);
  pinMode(zCd, INPUT_PULLUP);
  pinMode(btn, INPUT_PULLUP);
  attachInterrupt(0, setFlag, FALLING);//zero cross attach
  delay(500);
  //Serial.begin(9600);
}

const int Weld(int pre, int pause)  //Actual weld
{
  delay(400);
  myServo.write(60);
  zeroCrossingFlag = false; //set flag false and wait for next zero crossing
  while (!zeroCrossingFlag) {};
  delayMicroseconds(tgr_dly);                  //delay to trigger at peak sine wave
  digitalWrite(LED, HIGH);  // LED simulates welding
  delay(50);               //Pre weld
  delay(pause);
    zeroCrossingFlag = false; //set flag false and wait for next zero crossing
    while (!zeroCrossingFlag) {};
    delayMicroseconds(tgr_dly);
    digitalWrite(LED, HIGH);
    delay(150);
    myServo.write(6);
}
void ZCpulse()
{
  digitalWrite(pulse, HIGH);
  delayMicroseconds(10);//10 us every 10 milliseconds
  digitalWrite(pulse, LOW);
}
void loop()
{
  btnState = digitalRead(btn);
  while (!zeroCrossingFlag){}

  // Test for button pressed and store the down time
  if (btnState == LOW && buttonLast == HIGH && (millis() - btnUpTime) > long(debounce))
  {
    btnDnTime = millis();
  }
  zeroCrossingFlag = false;
}

void setFlag()
{
  zeroCrossingFlag = true; //interrupt sets flag true
}

anishkgt–You need to back up a step and learn how to use ServoTimer2.h before trying to integrate it into the welding code. Study the library examples, and perhaps examine the underlying library .cpp and .h.

First, you need to remove the usage of Servo.h and create a servo object for the ServoTimer2.

//#include <Servo.h>
#include <ServoTimer2.h>
#include <TimerOne.h>
ServoTimer2 myServo;

I’m not familiar with ServoTimer2, but from what I can see, it takes pulse length as write() commands and does not take degrees like Servo.h. The pulse widths have library default minimum and maximum values, and I’m not sure how they relate to your servo and what positions it will reach.

Understand the new library first, and then work to integrate it.