The Chopper

The goal of this project was to be able to perform dimming of an incandescent lightbulb. These ligthbulbs run off AC and in order to control how much light they emit, the input AC wave must be controlled. As the picture shows, the bulb can receive full waveform for full brightness, half the waveform for half brightness and so on*. In order to correctly control the final waveform for the bulb, the controller needs to know when the AC wave has crossed the zero point so that it can wait a certain amount of time and let the rest of the wave through until the next zero cross. *Not exactly true, more on this later.

Electrical components are needed to perform the switching and the zero cross detection, those will be discussed in detail in the next section. This section will focus on the control and code part of the problem.


Real Time Control; ISRs

Real time control software typically runs on interrupt service routines (ISR); a signal triggers the beggining of the control code so that it runs at a known time. A piece of hardware will take care of looking at the AC wave and outputting a signal to the controller when the AC wave crosses the zero point, this will be the trigger to the zeroCrossISR(). The AC wave being measured is 120VAC at 60Hz so this interrupt will run at 120Hz.
Meanwhile, another interrupt is running on the background: checkDimState(). This ISR runs at 15.36kHz and can turn on the switch for the sine wave at 128 different points in half a sine wave, allowing for a wide range of brightness options.
The other functions are not time critical and take care of checking other states like button presses or state updates.


Coding acrobatics

The main hurdle for getting the code to work was to get it to perform the control code in the time alloted. The processor that was chosen for this task is an ESP8266. This processor is cheap and can handle WiFi easily but real time control on it is not very straightforward. The timer libraries that exist for this processor can't run fast enough so instead I used some less documented functions and put the relevant high speed functions in RAM using the ICACHE_RAM_ATTR directive.
Fortunately, this was enough for the loop to run in time and check through all the switches (the lightbulbs will be connected to a socket) to see if it's time to turn the triac on or off.


The Details

Because we're regulating how much brightness we want the bulb to emit, we're really worried about how much power is being transmitted to it. For simplicity's sake, that is the area under the AC curve. This means that dividing half an AC wave into 128 intervals and calculating 90% dimmed to be 0.9*128 actually gives us 90% of the way to 128, not 90% of the area under the curve. To figure out 90% of the area, we need to use some good old integration.
The area for half a sine wave is 2 and the area under a sine wave from zero to x is 1-cos(x), where x is in radians from zero (beggining of zero cross) to π (next zero cross). Next, we can solve for x, get the delay needed in radians and then use that to normalize the result to be proportionate to the 128 dimming levels. dimLvl is the percentage brightness for the bulb.
This calculation will be used later on when we're commanding a brightness.