[SOLVED] Voltage controlled AC Dimmer

Hey everyone!

I'm not sure if this is the correct category, so I appologize if I've made a mistake.

I'm trying to build an AC dimmer circuit that works on 220V 50Hz as per these 2 tutorials:

The problem with these tutorials (and all other examples, libraries and other information I found online) is that it is controlled by phase cut percentage and not by the actual voltage percentage. For example (assuming we have 220V AC): 50% does not mean 110V. Instead, both in simulation (Proteus) and in a real circuit I get 157V.

Here's the current code I'm using for testing purposes:

#define TRIAC 3

void zero_crossing()
{
  delayMicroseconds(map(analogRead(A0), 0, 1024, 0, 10000));
  digitalWrite(TRIAC, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIAC, LOW);
}

void setup() 
{
  pinMode(A0, INPUT);
  pinMode(TRIAC, OUTPUT);
  
  attachInterrupt(digitalPinToInterrupt(2), zero_crossing, CHANGE);
}

void loop() {}

And here's the circuit:

Yes, I know the code is not optimal and I should do as little as possible in an interrupt routine, but this is just for testing purposes. I'll replace the delay functions with actual timer interrupts in the future.

You might've noticed that I use a value of 1024 in the map function, and that's because of the following article, but it should make much of a difference:

Another thing I want to mention is the attachInterrupt function. I use an "on CHANGE" mode because I'm using LM393 for the zero crossing detection. If I replace it with 4N25 (and of course, a bridge rectifier) I should use a RISING mode pin interrupt. I've tried both LM393 and 4N25 zero crossing detectore, but the results are the same.

Finally, I should also mention that when testing in real life, I use BT139 triac instead of the one shown on the image above.

So now, my question is:

Can I somehow calculate the amount of delay needed to achieve a given voltage?

Here's an example, just to make things extra clear:

#define TRIAC 3

uint16_t triacDelay = 0;

// The parameter voltage has a range of 0 to 220
uint16_t get_delay_from_voltage(uint8_t voltage)
{
    // Calculate the delay according to a given voltage
    uint16_t delayAmount = 0;

    // Perform the actual calculations here
    // ...
    // ...
    // ...

    return delayAmount;
}

void zero_crossing()
{
  delayMicroseconds(triacDelay);
  digitalWrite(TRIAC, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIAC, LOW);
}

void setup() 
{
  pinMode(TRIAC, OUTPUT);
  
  attachInterrupt(digitalPinToInterrupt(2), zero_crossing, CHANGE);
}

void loop()
{
    // The code in the loop function alternates between 100v and 200v each 5 seconds.

    // This means that we should have an output voltage of 100 volts
    triacDelay = get_delay_from_voltage(100);

    delay(5000);

    // This means that we should have an output voltage of 200 volts
    triacDelay = get_delay_from_voltage(200);

    delay(5000);
}

Making a lookup table has crossed my mind, but I don't think it's the most optimal solution, because of the need to test 220 values and create the table according to the them.

I'd gladly provide any additional information needed and I'd greatly appreciate any help.

Thanks in advance!

Put on your thinking (math) hat. The phase is the phase of a sine wave. It's a mathematical function. Every trigonometric function has an inverse. That's all I will say.

Edit - except, how did you come up with the value 220 for the number of table entries in the lookup table? I do hope it has nothing to do with the voltage...

Not sure what you mean, "optimal". Or "test 220 values". If you have to make a lookup table, it's done only once at boot time. There is no testing, it's all calculated.

Also if you seek perceptual brightness changes, you have to factor gamma into the luminance function.

Thank you for the quick reply!

I understand that the phase is a sine wave, and it's inverse can be found with arcsin. And if I'm not mistaken, 50% of the sine wave, is 50% of the arcsine, which means I'll still get 157 volts when I have 50% of the phase cut, and not 110 volts (50% of 220v).

About the "testing 220 values". I will need to test the amount of delay for 0 volts, then for 1 volt, then for 2 volts, and so on up until 220 volts. (Yes, technically I'll need 221 values in the lookup table). Then store those value in an array in the code and if I need, lets say, 100v, I will get the 100th element in the lookup table, which stores the amount of delay to get the desired 100 volts.

And no, I do not seek perceptual brightness, but thanks for the suggestion anyways.

Why? What is so important about 1V steps?

I'm on my first coffee of the day, but I think you are mistaken. Did you try?

Why? What is so important about 1V steps?

In the future I want to be able to set a maximum voltage for the lamp, let's say 180 volts (it will be adjustable), and then calculate a percentage based on that maximum voltage. For example, if I set the maxmium voltage at 180v, and I need 35% brightness, I will need to calculate 35% of 180v which is 63v. Therefore, I will need 1v steps, thus the 220 values in the lookup table.

I'm on my first coffee of the day, but I think you are mistaken. Did you try?

I didn't try it, but I plotted the graph on desmos, and the max extrema of the sin function has the same x value as the max extrema of the arcsin function, so I assume that if I take the first 50% of the sin function (from 0 to PI/2) is the same as taking the first 50% of the arcsin function (from 0 to PI/2) which will result in those 157 volts.

I'm sorry, I just don't understand how, and I've been stuck on this for almost a week now.

That is purely a scale factor, it has nothing to do with the number of interpolation steps. Your logic is flawed.

About the linearization of the brightness function, really I need the second cup of coffee... I'm not that sharp until then...

I wonder if the OP understands how a triac works.
It doesn't reduce the voltage, it's nothing like a transformer or a resistor.
Its output is not a reduced sine wave, it's the remnant of a sine wave.

Yes, and the energy delivered is the integral of the sine wave over the active interval.

I'm aware of this, I've been looking at it on an oscilloscope, and that's why I mentioned that I am cutting the phase with the triac. Cutting the phase 50% does not output 50% of 220v, but 157v.

What is that supposed to mean?

Yes sin(45) * 220 = 155.5V

That does make sense. In fact, I found this article which does exactly what you are saying:

https://www.hackster.io/353583/tiny-isolated-dimmer-with-linearly-corrected-output-f5acb5

And tested it out. But as you can see, even with 10 steps, 50% gives a delay of 5ms, which does not output 110v (50% of 220v).

Stick with one issue at at time. First deal with the linearization.

Does it matter that "220vac" v_pk is 311v ?

LOL that too.

I'm sorry for not being clear enough. I'm using a leading edge dimmer, and by 50% i meant that i cut the sine wave from 0 to PI/2, and then I turn on the triac from PI/2 until PI.

Okay, now I think that I see the problem.

I've been working with 220v, not taking 311v into account.

I'll test it out like that, and come back with a reply.

Thank you for the answers! I really appreciate it.

No that is a complete red herring and a waste of time.

But it does make sense, because 50% of 331 is 155.5v, which is pretty close to what I'm getting as an output.