Arduino Code for Pulse Skip Modulation (PSM)

I have recently bought this light dimmer, and only when I received it I noticed it works with PSM (Pulse Skip Modulation) and not PWD (Pulse Width Modulation).

Does anyone know how to write Arduino code and do the wiring for this dimmer? I can't seems to find a working example.

Thanks

• The module is basically a triac with some support components. So instead of PSM, you could do Phase cutting (leading edge cutting). This is where you turn on the triac somewhere during the sinewave. Once the triac is turned on, it stays on till the next zero crossing. So you use the zero crossing signal to know when the next cycle starts, and you then turn the triac on somewhere in the cycle. Just note that since you are chopping up the sinewave the resulting brightness will not be linearly proportional to the percentage of time it's turned on. – Gerben Apr 15 '18 at 18:49
• Also, mains led lights that support dimming do so by measuring the waveform that's coming in and calculate the PWM value based on than. Since the main part of an led is a bridge rectifier, and a smoothing capacitor. Using a regular led bulb on a dimmer will have very little effect, right until the dimmer is almost fully off. – Gerben Apr 15 '18 at 18:53
• I actually also recently bought this same dimmer switch. Is there any way to vary the intensity through more than high and low intensity? Is there a way to have let’s say 25-30 intensities of getting dimmer or brighter? I wanted to set some sort of counter on it so that over a span of like a minute it could decrease from full brightness to completely off. Also does the Z_C pin have to be a PWM pin on the Arduino? Thanks for all the help! – timshine May 12 '18 at 20:30

I assume you use this module.

PSM is often used because of it's higher efficiency with light loads. It's also better for AC loads, since they should get a complete AC voltage and not a DC voltage (when you cut half of the wave).

The module you have, is build to support you with implementing PSM correctly. It has a zero-crossing detection, which gives you a signal on the Z-C pin, every time the AC sine wave crosses zero voltage (which is the best time to switch). You can sense the signal with an interrupt in your Arduino. The website does not explain exactly, what is outputted by the Z-C pin on a zero crossing, but you can check this easily yourself, by monitoring both AC and Z-C voltage in operation. Most likely you will have to use either a FALLING or a CHANGE interrupt.

PSM works with modulating the number of pulses (in this case full sine wave periods) are transmitted to the load. Imagine you want to dim the light in 10 distinct values.

• For full power you always set the pin marked with PWM to high, when a full sine wave is starting (this is at every second zero crossing). This means the PWM pin is always high.
• For half power you turn the PWM pin high for 5 full periods and then turning it off for further 5 periods.
• For 10% (a value of 1) you turn the PWM pin high for 1 full period and then off for 9 periods.

The downside of this method is, that you might see the light flickering when having low values.

When implementing this, you can use the attachInterrupt() function with the Z-C pin. In the callback function you toggle a global (volatile!) boolean variable, to indicate, if a new full period is starting, or if the zero crossing signal is only indicating the start of the negative part of the sine wave. Also use a global (volatile!) counter variable, that counts (increments every full period) to the value, you preset (Here something between 0 and 10). When the counter variable is 0 (and the desired value is greater than 0) you write HIGH to the PWM pin of your module. When the counter reaches the desired value, you write LOW to the PWM pin. When the counter reaches the maximum value, you have reset it to 0.

In code something like this:

volatile byte counter=0;
volatile boolean new_period = true;
volatile byte value=5;
#define MAX_VALUE 10

void setup(){
// set pinModes for the correct pins

// active interrupt. Note, that you may have to change FALLING to a value fitting the output of the module (see explanation above)
attachInterrupt(digitalPinToInterrupt(Z_C_pin), zero_crossing, FALLING);
}

void zero_crossing(){
if(new_period){
if(counter==0 && value>0) digitalWrite(PWM_pin,HIGH);
if(counter==value) digitalWrite(PWM_pin,LOW);
counter++;
if(counter > MAX_VALUE) counter=0;
}
new_period=!new_period;
}


Note: Especially when having a great range of possible values, some of the values can be exchanged by a better behavior, that would prevent a bit of the flickering. For example, when using 100 distinct dimming stages and wanting to dim to 50%, you can either (as depicted above) turn the light on for 50 periods and then turning it of for another 50 periods. Or you can turn on during every second period. Though this will not help you with really small values. So I left it out for simplicity.

• Amazing answer. Works perfectly... – Guy Sopher Apr 15 '18 at 20:29
• @chrisl Hi while I try on Mega 2560 with attachInterrupt(0, zero_crosss_int, RISING); or attachInterrupt(digitalPinToInterrupt(interruptPin), zero_crosss_int, RISING); I am getting exit status 1 Error compiling for board Arduino/Genuino Mega or Mega 2560. error. How to solve it? – Alper Feb 11 '19 at 14:40
• The full error code is; WInterrupts.c.o (symbol from plugin): In function attachInterrupt': (.text+0x0): multiple definition of __vector_5' libraries\RBDdimmer\RBDdimmer.cpp.o (symbol from plugin):(.text+0x0): first defined here c:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/5.4.0/../../../../avr/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions collect2.exe: error: ld returned 1 exit status exit status 1 Error compiling for board Arduino/Genuino Mega or Mega 2560. – Alper Feb 11 '19 at 14:47
• The RBDdimmer library seems to also define the corresponding ISR, which cannot be done twice. Have you tried other pins? The Mega 2560 seems to have 8 external interrupts and the RBDdimmer library is locked to D2 as of the github page. – chrisl Feb 12 '19 at 7:44
• All ports (2, 3, 18, 19, 20, 21) results with the same error.. – Alper Feb 12 '19 at 14:06

There is a simple example on the manufacturers website of this module which does not seem to implement PSM the obvious way:

/*
Connect RobotDyn AC Light Dimmer as follows:
Dimmer pin  --  Arduino pin
VCC     --    5V
GND     --    GND
Z-C     --    D2
PWM     --    D3

Glow sketch for AC Voltage dimmer with Zero cross detection
Based on the sketch by Charith Fernanado
Attach the Zero cross pin of the module to Arduino External Interrupt pin
Select the correct Interrupt # from the below table (the Pin numbers are digital pins, NOT physical pins: digital pin 2 [INT0]=physical pin 4 and digital pin 3 [INT1]= physical pin 5)

Pin    |  Interrrupt # | Arduino Platform
---------------------------------------
2      |  0            |  All
3      |  1            |  All
18     |  5            |  Arduino Mega Only
19     |  4            |  Arduino Mega Only
20     |  3            |  Arduino Mega Only
21     |  2            |  Arduino Mega Only

In the program pin 2 is chosen
*/
int AC_LOAD = 3;    // Output to Opto Triac pin
int dimming = 128;  // Dimming level (0-128)  0 = ON, 128 = OFF

void setup()
{
attachInterrupt(0, zero_crosss_int, RISING);  // Choose the zero cross interrupt # from the table above
}

// the interrupt function must take no parameters and return nothing
void zero_crosss_int()  // function to be fired at the zero crossing to dim the light
{
// Firing angle calculation : 1 full 50Hz wave =1/50=20ms
// Every zerocrossing thus: (50Hz)-> 10ms (1/2 Cycle) For 60Hz => 8.33ms (10.000/120)
// 10ms=10000us
// (10000us - 10us) / 128 = 75 (Approx) For 60Hz =>65

int dimtime = (75*dimming);    // For 60Hz =>65
delayMicroseconds(dimtime);    // Off cycle
delayMicroseconds(10);         // triac On propogation delay (for 60Hz use 8.33)
}

void loop()  {
for (int i=5; i <= 120; i++){  // We use 120 as the lowest and 5 as the highest brightness setting. May be adjusted depending on the lamp used.
dimming=i;
delay(20);
}
for (int i=120; i >= 5; i--){  // Same as above
dimming=i;
delay(20);
}
}


They are using an interrupt as suggested by chrisl. There is no flickering present with low dim values (using an incandescent bulb). See this video for a similar board with the original version of the above sketch.

Note: If you want to integrate your own code into this example, it will likely not work as intended because the Arduino is busy with delayMicroseconds() most of the time

EDIT: The manufacturer of this module just released a library. Check it out on their github

This module allows you to implement pulse skipping modulation but also phase angle control. The code that reins.ch posted is actually the latter.