Replacing Delay

Hello Everyone :slight_smile:
I am new to Arduino & its Programming. A few days back i managed to build a TRIAC based circuit using arduino UNO to control the brightness of a bulb. UNO recieves input pulse from a zero crossing detector circuit and outputs a gate pulse to a TRIAC after a specified amount of delay.
At every zero crossing a pulse is generated, as soon as the UNO gets this pulse, it outputs a gate pulse after a certain amount of delay which decides the conduction angle and hence the brightness of bulb.
According to the line frequency, a pulse is generated every 10ms. So a delay of 9ms lights the bulb to minimum brightness (as TRIAC conducts for only 1ms and thus minimum power gets delivered to the bulb) and similarly a delay of 1ms lights the bulb to its maximum brightness.
Here's the Sketch:

int outPin = 5; // Pin for Output Gate pulse for TRIAC
int inPin = 4;   // Input from Zero Crossing Detector
int val = 0;     // variable to store the read value

void setup()
{
  pinMode(outPin, OUTPUT);      // sets pin 5 as output
  pinMode(inPin, INPUT);      // sets pin 4 as input
}

void loop()
{
  digitalWrite(outPin, HIGH);
  val = digitalRead(inPin);   // read the input pin
  if(val == HIGH)
  delay(9);
  digitalWrite(outPin, LOW);  
  if(val == HIGH)
  digitalWrite(outPin, HIGH);
}

Now the problem is : I WANT TO USE 2 BULBS :sweat_smile:
So if i use 2 output pins for 2 Gate pulses of 2 TRIACS with 2 Delays, both the delays interfere with each other and i get some unwanted flicker in both the bulbs (of course due to 2 delays).
I also tried to accomplish the same using millis() which i used 1st time in my life, but still failure. This didn't succeeded even for a single bulb. Here's the sketch with millis() instead of delay:

int outPin = 5; // Pin for Output Gate pulse for TRIAC
int inPin = 4;    // Input from Zero Crossing Detector
int val = 0;     // variable to store the read value
long previousMillis = 0;
long interval = 8;

void setup()
{
  pinMode(outPin, OUTPUT);      // sets pin 5 as output
  pinMode(inPin, INPUT);       // sets pin 4 as input
}

void loop()
{
  unsigned long currentMillis = millis();
  digitalWrite(outPin, HIGH);
  val = digitalRead(inPin);   // read the input pin
  if(val == HIGH)
  if(currentMillis - previousMillis > interval) {
  previousMillis = currentMillis;
  digitalWrite(outPin, LOW); 
  if(val == HIGH)
  digitalWrite(outPin, HIGH);
}
}

Using the above code lights up the bulb to a very very very low brightness. Much less than minimum brightness in the 1st sketch using delay. But changing the interval in above sketch doesn't affect the brightness of bulb, however making interval more than 9ms turns the bulb completely off.
Hope you people will figure out whats wrong here. Thanks in advance. :slight_smile:

Foes formatting your code (for you) help you to see the problem?

void loop()
{
  unsigned long currentMillis = millis();
  digitalWrite(outPin, HIGH);
  val = digitalRead(inPin);   // read the input pin
  
  if(val == HIGH)
      if(currentMillis - previousMillis > interval) {
          previousMillis = currentMillis;
          digitalWrite(outPin, LOW); 
  
          if(val == HIGH)
              digitalWrite(outPin, HIGH);
      }
}

The third if is redundant, as val is not change, which leaves...

   if(val == HIGH) {
      if(currentMillis - previousMillis > interval) {
          previousMillis = currentMillis;
          digitalWrite(outPin, LOW); 
          digitalWrite(outPin, HIGH);
      }
  }

...Which I would guess, is not what you were intending to do.

Thanks Matt. :slight_smile:
I removed that third if

However, the problem still remains the same

You are toggling outPin from low to high, immediately. I would guess you would want to hold outPin low for some_time.

I don't know too much about mains electric. Messing with 250V AC, scares me to death. My wife has told me I must build her a dimming thermostat at some point. Hmm. Saying no to my wife, even more scary :smiley:

LOL. Thank God i am Single XD
A small pulse is enough to trigger the TRIAC. A delay of 1ms doesn't make a difference either.
Thanks for your efforts Matt.
But Still same problem :cold_sweat:

Hang on, lets go back and structure your original code.

void loop(void) {
  digitalWrite(outPin, HIGH);
  if(digtialRead(inPin) == HIGH) {
    delay(9);
    digitalWrite(outPin, LOW);  
    digitalWrite(outPin, HIGH);
  }
}

What you have there is a delay of 9ms, between inPin going high and driving outPin low.

That is not what your later code does. You initialised previousMillis = 0, so as soon as inPin goes high, currentMillis - previousMillis is bound to be greater than interval, so the condition is true and there is no delay. You then assign currentMillis to previousMillis and if currentMillis - previousMillis turns out to be true again, it's a coincidence of timing, rather than what you were intending.

Off the top of my head, because I don't have a compiler handy, I suspect you intended something like this...

uint16_t now = 0;
uint16_t startWaiting = 0;
boolean waiting = false;

void loop(void) {
  now = millis();
  digitalWrite(outPin, HIGH);
  if ((!waiting) && (digitalRead(inPin) == HIGH) )  {
        startWaiting = now;
        waiting = true;
   }
   else if (waiting && ((now - startWaiting) > 8)) {
      digitalWrite(outPin, LOW);
      waiting = false;
   }
}

I spent 22 years happily not married to my now wife....Just to be sure :smiley:

OK Matt.
After thinking for hours, studying integer types and boolean operators, i understood my mistake and also understood how smartly you rectified it.
Now my problem solved to an extent. The brightness of bulb does change by changing that number which we are comparing with (now - startWaiting), but the bulb keeps on flickering a bit. I mean it is not stable as with the previous sketch which used delay.
Now 2 question pop up in my mind,

  1. Why is it flickering?
  2. How will it be stable?

Matt, Thanks a lot for you help. Its so kind of you.

Did I mention I don't do AC :wink:

The flickering might be down to latency. millis, digitalRead and digitalWrite are relatively slow to execute.

I may have been a little too purist in letting the loop reset outPin.

Try altering the else statement to drive the outPin HIGH immediately.

   else if (waiting && ((now - startWaiting) > 8)) {
      digitalWrite(outPin, LOW);
      digitalWrite(outPin, HIGH);      
      waiting = false;
   }

BTW, would you happen to have a circuit diagram and some part numbers you could point me at.
I will have to have a go at that thermostat one day.

Really Sorry for being late, Matt.
Here's the Circuit diagram with all part numbers. Note that R13 must be of 1W or more and C3 must be able to withstand 240V .
Circuit diagram is for 1 bulb only, if you want two, use 2 outPins, 2 MOC3021's, 2 Triacs and so on.
I will try altering the else statement and update you soon.
You are really helpful Matt, i appreciate your kindness.

No need to apologise and thanks for the circuit diagram. I can see it saving me a lot of googling.

I help out here when I have some idle time and think I can add something. I don't like doing cross words!

The flickering you see could be jitter caused by the millis() function. One half cycle is only 10 milliseconds @ 50 Hz. +/- 0.5 ms jitter could probably cause noticeable brightness variations. Try using the microsecond timer micros() in the Arduino to do your timings. It might reduce your jitter problem, and will give you greater resolution over your conduction angle.

One thing I noticed on your schematic is the lack of a pullup resistor on ZDIN pin. When the optocoupler turns off at the zero-crossing, there's nothing to pull it high reliably.

I would use a finite state machine for this. Your machine is basically in two states, so it's fairly simple: waiting for the zero crossing, and delaying the conduction angle. For controlling one bulb, I'd go with something like this:

const byte inPin = 4;
const byte outPin = 5;

unsigned long zero_crossing_us = 0;
unsigned long firing_delay_us = 8000;

enum {
  WAIT_FOR_ZERO,
  DELAY_FIRE_ANGLE } system_state;
  

void setup()
{
  pinMode( inPin, INPUT_PULLUP);
  pinMode( outPin, OUTPUT );
  digitalWrite( outPin, HIGH );
}

void loop()
{
  byte zd_input = digitalRead( inPin );
  unsigned long now = micros();
  
  switch(system_state)
  {
    case WAIT_FOR_ZERO:
      if( zd_input == HIGH )
      {
        // Zero crossing detected, store timestamp and change state.
        zero_crossing_us = now;
        system_state = DELAY_FIRE_ANGLE;
      }
      break;
      
    case DELAY_FIRE_ANGLE:
      if( now - zero_crossing_us > firing_delay_us )
      {
        // Conduction angle delayed out, fire TRIAC and change state.
        fire_TRIAC();
        system_state = WAIT_FOR_ZERO;
      }
  }
  
}

void fire_TRIAC()
{
  digitalWrite( outPin, LOW );
  digitalWrite( outPin, HIGH );
}

It compiles. Whether it works or not you'll have to test.

A quirk of mine you might notice in the code is that I don't like to mix inputs with logic. I prefer to get all the inputs up front, and then have the program operate on the variables.

Here are a couple of references about state machines:

http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html