Dimmable AC Light Box

From versions 0.5 to 0.7 of the code I went through assembly hell as I kept updating the code trying to get UART to work. As one could imagine, the process of removing the IC every few minutes to test code became tedious, and I started bending pins. I knew what I had to do. I had to implement ICSP so I wouldn’t have to worry about removing the IC from the board. It was also around this time I felt confident enough to make a logic board, so I did.

I ended up assigning version 0.6 to code that would not even execute as a way to save what changes I had been making before making other changes.

The problem I had was configuring the half dozen registers needed to get UART to function correctly, and actually….Reading-TFM. I wasn’t configuring the SPBRG register, and it is required to time the UART speed. The SPBRG register also needs to be calibrated differently based on the clock speed, which I hastily obtained the wrong value for. I also ended up using 3 interrupts. One for the hardware Zero Crossing detection on INT-0, another for the UART Receive, and the last for a timer for the Zero-Crossing offset. Configuring the registers to allow these interrupts, along with configuring and understanding the concepts like the “pre-scaler” took MANY hours of skimming through data sheets.

Also, my friend, the Interrupt Vector. :D

8-Bit PICs only have one. So what is an Interrupt Vector, you may ask? It’s a memory location where execution jumps to whenever an interrupt is triggered. As all three Interrupts jumped to the same address, I was forced to compare registers to see which Interrupt called execution to jump there.

ALSO, the registers being worked on when interrupted DO NOT automatically become saved. First thing I would do in the Interrupt routine is SAVE all work registers to temp addresses. I used Microchips’ own example to do this. Upon completion of the Interrupt routine, I would then restore the register values.

 org    0x0004                    ; memory location h004
; ***************************************************************************
; Interupt Service Routine    MUST RUN FROM HERE ONLY !
; First run the Context Save Registers   – you need to create the _temp files

movwf    W_ISR_TEMP            ; copy W to temp
swapf    STATUS,W            ; swap status to be saved into W
clrf    STATUS                ; bank 0
movwf    STATUS_ISR_TEMP        ; save status in bank O
movf    PCLATH,W            ; save pclath
clrf    PCLATH                ; clear to page 0 for ISR
movf    FSR,W                ; save FSR
movwf    FSR_ISR_TEMP

movf    FSR_ISR_TEMP,W
movwf    FSR
movwf    PCLATH
swapf    STATUS_ISR_TEMP,W    ; swap status temp to w to set org bank
movwf    STATUS                ; movW to status
swapf    W_ISR_TEMP,F        ; restore W
swapf    W_ISR_TEMP,W
retfie                        ; return from isr

; End of ISR



I built out the box around this time over the course of a few weeks. I took my time, and collected the hardware I needed from Skycraft, Radioshack, Mouser, and Home Depot. As safety was excessively on my mind, I used high grade electrical wire to feed power to the box, and grounded both the top, and the bottom of the box.

I chose to house the Triacs on a perfboard, with an electrical power bus soldered to the bottom of the board (Which I unfortunately do not have a picture of.)

I used 10 fuses.

  • A 10-amp fuse for the Triac board that drove all the outputs.
  • An individual fuse per each of the 8 channel.
  • Another for for the  transformer that drives the zero crossing detection, and 5v rail.


The triacs are by STMicroelectronics, Model BTA16-600B, and they are being driven by Fairchild MOC3021 opto-isolators. After reading through lots of example circuits, I decided to go with the minimalistic, resistive only load circuit found in the datasheet for the MOC3021M.


I started mounting the components, and although not visible from the top of this PCB, a striped 16 gauge, stranded electrical wire runs the horizontal length of it, soldered on with a 100 watt soldering gun. I used this as a power bus. I then soldered bare 18 gauge, stranded wire that runs from this bus to each Triac, and then to the output screw terminals. The screw terminals at the front of the board are digital I/O inputs for each channel, and for power and ground.


To drive the opto-isolators, I used NPN transistors to switch their ground pins, and drove the transistors from the PIC Micro.


Below is the box with power and zero crossing detection board on left wall, logic board on back wall, and triac board coming off the base of the box.


Different angle with transformer in upper right.


With the box safely assembled, and without AC mains cable hanging everywhere, I concentrated on optimizing the assembly code….

Pages: 1 2 3 4 5 6

About waterbury

Hi, you may also know me by my IRL name, Ted.