Introduction: Cheap & Easy Opto-isolated TTL-to-110V Light Control Module

This Instructable shows how to convert store-bought light-controlled outlets into low cost control modules for safely driving 110V loads with 5V (TTL) or other low-voltage control signals.

Motivation

I wanted a way to completely isolate 110V high voltage circuitry from a low voltage drive circuit. I also wanted to do it inexpensively with easily available parts. The heart of the device is a light-controlled outlet available for $6.95. The ambient light input is replaced by a small red LED. So this is an affordable way to get into light control for Christmas displays, stage lighting, or any electrical load where you need only on/off control. The components for this 2-channel example cost $17.90, not including the various wires which came from scrap.

Step 1: Background

The photo shows one of the light-controlled outlets used for this project.

Light-controlled outlets are normally used to light a nightlight when it gets dark. By mounting a red LED directly above the light sensor you can use any digital circuit to drive a 110V load safely opto-isolated from your low voltage control circuit. This Instructable shows a 2-channel module built into an outlet box, but the concept can be expanded to any number of channels in any packaging format. I use a pair of compact 8-channel modules to drive an outdoor Christmas tree display using a Basic Stamp microcontroller.

The light sensor inside these devices is a cadmium sulfide (CdS) light-sensitive resistor which is most sensitive to red-orange light, so light from a low-output red LED is sufficient to make the outlet turn on. The LED can be driven from any control source.

This Instructable shows you how to modify the light-controlled outlet, converting it into a simple module that has a pair of wires for 110V control and a pair of wires to drive the LED. If you don't want to do the "circuit bending" required to make this module, you can use the light-controlled outlet as-is by merely mounting the LED over the sensor externally. Then you can plug as many modules as you want into a power strip and run your LED wires to a suitable controller.

This Instructable does not cover the controller that drives the LEDs. That is left to your imagination. Your controller could be any kind of digital circuit from a bank of switches to an interface you plug into a PC. At the bottom of this Instructable I include an overview of my simple Basic Stamp microcontroller circuit with source code for driving a 16-channel Christmas tree.

Step 2: Parts and Tools

Parts list for one twin-outlet controller module:

2 ea. light-controlled outlets, available at Wal-Mart for $6.98 each, called
"Automatic Plug-In Light Control", UPC code 18258
1 ea. deep plastic outlet box, about 3.25 inches deep, $0.43 at Lowe's
1 ea. 110v receptacle, 15A, $0.49 at Lowe's
1 ea. Steel outlet cover, $0.54 at Lowe's
1 pkg. of 2, red LEDs, 3mm, Radio Shack p/n 276-026, $1.49
1 pkg. of 5, 330 ohm, 1/8 watt resistors, Radio Shack p/n 271-012, $0.99

misc: small tie-wraps, electrical tape, wire nuts

Total cost without sales tax: $17.90, not including various hookup wires and "misc" parts.

You can get two channels for just under $20, less if you can find light controllers on sale, they are the "big ticket" item. I did not price the 110V hookup wire because the type and length of 110V wiring you use will depend on how you connect this module to the rest of your display. For that reason I also did not include any connectors for the control wires. The wiring shown here is sufficient for testing the module.

Note: you cannot use typical outdoor lighting photo controllers like the Intermatic "Photo Control" p/n K4021C, available at Lowe's for $6.98, and that's too bad because they would be so much easier to modify this way. They look tempting with their nice pigtail leads and obvious CdS photocell input. However, those controllers are thermally actuated and take about a minute to turn on and off. So unless you want a really slow display, stay away from them.

I used these very ordinary tools:

screwdrivers
soldering iron
file
vise
wire stripper
diagonal wire cutter
needle-nose pliers
continuity tester
hot glue gun
drill with 3/16 inch bit and 1/16 inch bit

Step 3: Open the Light-controlled Outlet

Remove the screw between the 110V prongs and set it aside. Put the module length-wise, prongs up, into a vise and slowly squeeze it until the case pops open. If you don't have a vise you can use a clamp, but whatever you use, don't squeeze it too hard and crack the case. If you think you are over-stressing the case you can help it pop open by prying the seam with a small screwdriver. Once the case pops, remove it from the vise and slide the top off the prongs.

Step 4: Desolder the CdS Cell

The CdS light sensor in this module is actually outside the case, down in a little well. This means you can't easily remove the circuit board from the case to work on it without first desoldering the CdS cell leads. The picture shows the location of these leads. You actually can complete this project while leaving the CdS cell attached, I removed it for this Instructable to make the rest of the modification process easier to document. The second module in this project was completed without disconnecting the CdS cell.

Step 5: Remove the Two External 110V Prongs

The 110V fittings are not used, so they are removed to make the module more compact and to create places to attach the 110V wiring. There are two large brass prongs which are the external 110V connection, and two copper-colored leaf springs which are the internal 110V connections. All four of these must be removed. The first step is to drill out the rivets holding the large brass prongs. Referring to the picture, use a 3/16 inch drill bit to cut away one side of each rivet. Only remove the protruding lip of the rivet then stop. The prongs will remain attached by a little bit of solder. To remove a prong, heat the solder and push the prong sideways. Be careful not to tear the printed circuit traces. The red circle in second photo shows a trace where one of the prongs was removed. If you do tear these traces don't worry too much, it just makes a later step where you add wires a little more finicky.

Step 6: Remove the Two Internal 110V Springs

The copper leaf springs are a bit harder to get off. I heated their soldered joints then quickly turned the board over and tapped it against the table to knock most of the melted solder off. You could also use solder wick. Once most of the solder is removed, you can see each spring is held in place by 3 twisted tabs. Twist the tabs so they align with their holes and you should be able to pull off the spring. You may have to heat the solder joint again.

Step 7: Drill Two Holes for the LED Leads

Referring to the picture, drill two small holes (about 1/16 inch) about 1/8 inch to the right side of the light sensor well. Make sure you put the holes on the correct side as shown.

Step 8: Mount LED Over Light Sensor

Bend the LED leads as shown in the picture so the LED drops into the well and the leads drop through the two drilled holes. Note which lead on the LED is the anode (positive lead, refer to the LED packaging) and mark the corresponding drilled hole. Put the LED into the sensor well so the exposed leads are flat on the top of the case. Fill behind the LED with hot glue or caulk or whatever you have.

Step 9: Make a Hole for the 110V Wires

To fit the module into the outlet box the way I did it in this project, the wires must come out the end of the module instead of coming out the existing holes. Remove one of the slats from the vent hole as shown. You can snip it off with little cutters or just pry it off at each end with a flat blade screwdriver.

Step 10: Place the 110V Wires

Cut a piece of zipcord (110V lamp cord) about 1 foot long (or use two pieces of stranded 18 gauge wire). Put a small tie-wrap about 4 inches from one end and insert the long end of the cord through the hole where you removed the slat so that the tie-wrap is on the inside of the case. The tie-wrap will act as a strain relief so you can't pull the cord out.

Step 11: Place the Control Wires

Take about two 1-foot lengths of small gauge stranded wire. I used a pair I pulled off a ribbon cable. The wires must be colored or marked so you can tell their polarity to drive the LED. Tie a knot in the wires about 4 inches from one end for a strain relief and pass them through one of the vent slots as shown.

Step 12: Attach the Resistor and Control Wires

Trim to length, strip and solder one of the control wires to a resistor lead near the resistor body. Bend a small loop in the other resistor lead near the body of the resistor (see first picture), slip it over one lead of the LED (it doesn't matter which) and solder it in place. If the resistor is attached to the positive lead from the LED, be sure to note which color that wire is and write it down. Trim, strip and solder the other control wire to the unused LED lead. Solder these wires as close to the components as possible. Trim away the excess wire and component leads from the solder joints. Bend the leads down flat to the bottom of the box on opposite sides of the sensor well. Keep these exposed leads away from the sides of the box, where the U-shaped metal heat sink goes. In the second picture the red rectangle shows the area to keep out of. Even though the heat sink is wrapped in paper, the exposed parts may be electrically connected to the 110V circuitry. You could pot the exposed LED/resistor leads with hot glue if you want.

Step 13: Route the 110V Wires

Run the two 110V wires through the holes where you drilled out the rivets as shown in the photo. The red arrow shows the upper wire at the vent and the rivet hole to feed it through so the wires will fit the best when the box is closed. Slide the circuit board down the wires all the way into the box.

Step 14: Trim the 110V Wires, Reattach CdS Cell

Trim off the 110V wires and strip them about 3/16 inch so they will align with the pads where they will be soldered, marked IN and OUT in the photo. One pad is the place where the wire comes through, and the other where one of the internal 110V spring connector was removed. After you trim the wires to length it may be easier to pull the circuit board off temporarily to strip the wires. Tin the ends of the wires and bend them so they will lay flat over their pads. Trim them so they won't extend past the pads when they are soldered down.

At this point I reattached the CdS cell leads, circled in red. Why? For no reason I can remember, now that I'm trying to make these pictures into an Instructable. I attached the leads to the side of the board facing up. If the holes are clean you could try to get the wires back up from underneath but it's not necessary. It also doesn't matter which lead goes to which pad, CdS cells are not polarized.

Step 15: Solder the 110V Wires

Solder the 110V wires in place, making sure you don't create any shorts to other circuit board traces. Referring to the pictures, mark the IN and OUT pads on the board, and also mark the far end of the 110V wires so you'll know which is which when the box is closed.

If you tore a circuit trace removing one of the brass prongs back in step 5 (like I did on the second module) you can still attach the 110V wire as shown in the second picture. You just have to be neater about it and be sure you don't make a solder bridge to one of the neighboring traces.

Step 16: File the Case Lid and Reassemble the Box

In order to fit two modules into the outlet box you have to file down the raised rims of the holes where the 110V prongs used to exit the case. Once the rims are filed off you can snap the lid back onto the case and put the screw back in. I also put a small piece of electrical tape over the exposed LED leads.

Step 17: Prepare the 110V Outlet

Outlet fixtures typically come with both outlets connected to be powered together. For this project each outlet will be operated separately, so you have to remove one of the brass tabs connecting them by bending it back and forth with pliers until it breaks off. The tab to break off is the one on the "hot" side of the outlet, which can be identified several ways: it's the one with the smaller prong slot, toward the side with the brass screws (if each side has different colored screws) and the one on the right if you look at the outlet with the ground hole towards the bottom. Or just refer to the pictures. The second picture shows what it should look like after the tab is removed. Be sure NOT to remove the tab on the other (neutral) side of the outlet. If you happen to do that don't worry, you can use a jumper wire to reconnect that side together. Number the outlets 1 and 2.

Step 18: Form the 110V Leads

The type and length of 110V wiring you use will depend on how you connect this module to the rest of your display. The wiring shown here is sufficient for testing the module. Since the outlets have a ground, you should use a grounded power cord even though the original modules do not include a ground.

Punch out one of the holes in the base of the plastic outlet box and run the 110V power cord end through. For strain relief I tied a knot in the cord about 4 inches from the end.

Picture 2: Break the wires out on the end of the power cord and prepare them as shown in the figure. Use a continuity tester to ensure you get the hot and neutral leads identified correctly. The 110V leads coming out of the modules will be connected as follows: The "IN" leads will be connected together to the 110V cord, the "OUT" leads will be connected individually to the outlet.

The ground and neutral wires on the power cord should be tinned and formed into hooks to make a neat connection to the outlet and the same for the 110V "OUT" leads from the modules (arrows).

The hot wire on the power cord should be left bare and the same for the 110V "IN" leads from the modules (circles).

Step 19: Connect It All Together

Twist the hot side power lead together with the input leads from the two modules, cap them with a wire nut and tie them with a tie-wrap for strain relief. Connect the neutral side of the power lead to the outlet's neutral side, which is the side where the two outlets are still joined by the tab. Connect the output of module 1 to hot side of the outlet marked 1 and similarly for module and outlet 2. Connect the ground lead to the outlet ground screw.

Step 20: Assemble Into the Outlet Box.

Feed the control wires for the two modules out through one of the openings in the bottom of the outlet box. Put the modules face-to-face (arrow) and push them to the bottom of the outlet box, they should just fit and leave room at one side for the wires to come into the box. Stuff the rest of the wiring in behind the outlet and screw it to the box. Mount the faceplate over the outlet and the assembly is finished.

Step 21: Test the Modules.

To test the module you'll need a table lamp or other 110V lamp and a battery somewhere between 3 and 6 volts. I used a 3V camera battery. Plug the project into a wall outlet. Plug a table lamp or other 110V lamp into project outlet #1 and turn the lamp on. The lamp should turn on because inside the module it is dark. Touch the leads of the battery to the control wires for module #1, being sure to get the correct polarity. The lamp should go out immediately every time you touch the control wires to the battery because you are illuminating the CdS cell inside the module with the LED. Repeat for outlet #2. If you get the battery polarity wrong it won't hurt anything, the LED just won't light. If you plug in the lamp and can't turn it on, the module is probably bad; I had one go bad this way.

Step 22: A Word About Power Capacity.

The modules used here are each rated to control 400 Watts. However, they are air-cooled via the vents, so with them packed into an outlet box like this you might want to limit the load to 200W on each outlet or drill vent holes in the outlet box to align with the vents in the modules. Also, make sure your 110V power cord can handle the maximum load you plan to put on both outlets combined. The amount of load will not affect the low voltage control side - it's opto-isolated and very safe.

Yeah, I didn't really have a picture for this step, but I like this "warning sticker for the unclueful".

That's the end of the main part of this Instructable. Depending on your application you might need to make more of these, and of course you will need to come up with a suitable control circuit. One possibility is presented briefly below, but it is more of a Slideshow than an Instructable since it was done before I knew of this site.

Step 23: Appendix: Going Much Further

Not sure if Instructables should have an appendix, but if I just posted this as a separate slideshow the context would be lost. So in my n00bie bliss I will add an appendix.

To make this light-controlled outlet concept really useful, you'll probably want more than just 2 channels. I started off with 8 unmodified modules plugged into a couple power strips with the LEDs added externally, but that quickly got unwieldy. So I removed the 8 module circuits from their cases and built them into a single box with 8 outlets along one side. Then I built another for a total of 16 channels.

Between the time I built my 8-channel versions and became aware of this Instructables site, Wal-Mart changed suppliers for the light controlled outlet and the new ones I used above do not have the same circuit board as I originally used.

Since I could not repeat the old project I decided to do the 2-channel version shown here just to convey the concept. The new modules are actually much easier to work with than the old ones, but there's no guarantee that Wal-Mart won't change suppliers again by the time you decide to build your project. So be flexible and keep in mind that the basic idea is just to replace the normal light input with the LED in a light-tight enclosure.

The photo shows the entire 16-channel setup with controller, in a weather-proof enclosure made out of an old toy box. The next steps will briefly describe these parts.

Step 24: Controllers

Having controllable channels is not much use unless you are controlling them with some kind of sequencer that you can set or program. That controller could be as simple as a panel of toggle switches and pushbuttons for a stage production or a museum-style interactive display, all the way up to a PC-driven card with a fancy GUI interface for a production timed to music. Although I won't go into construction detail in this "appendix", I will show my controller and its program code as an example.

My controller is somewhere between switches and a PC. I decided to use a Basic Stamp "Homework" microcontroller/protoboard mostly because I wanted to play with microcontrollers again and that seemed to be a popular modern choice with low cost and lots of online support. The only other choice I had on hand was an MCS-85 "System Design Kit" (Intel 8085, ca 1977) and support for those is thin these days. Plus you have to program it with a cheesy little hex keyboard every time you power it up. Another cool possibility I may yet build is something that reads holes in a paper tape like a player piano or a PDP-8. OK enough tech-fogey stuff.

What I am controlling these days is an outdoor Christmas tree decoration which is basically a maypole with 16 individually controlled strings of lights arranged in upper and lower rings of 8 strings each. The fun of building the hardware is over, but there is still the fun of programming new lighting patterns each year and cramming the code into the limited memory space of this particular model of Basic Stamp.

I also included pictures of the 16-channel controller setup, its weatherproof box, and the simple octal-latch interface between the Basic Stamp and the control modules. These pictures should give all the creative types here on this board a good head start on their own designs.

Step 25: 8-Channel Module

The concept here is the same as in the main Instructable, except the 8 light control circuit boards were removed from their original cases and mounted in one box. The LEDs are isolated from each other in individual wells with a hole that exposes them to their respective CdS cells. The photo shows one 8-channel module with the top off, sitting on the other module which shows the outlet side.

Step 26: Circuit Board

The photos show a modified circuit board. I turned one of the external 110V prongs into a screw terminal. This board is only rated for 300W, but times 8 that's 2.4KW, which I am unwilling to drive through a wood box. Also, the power cord feeding it is only good for about 1.2 KW, so I keep the load down to 2 strings of 100 mini-lights per outlet.

Step 27: Module Details

The CdS cells face holes in the back wall; on the other side are wells for the LED/resistor circuits (picture 2). The boxes are made to be opened and the 110V leads both run through screw terminals because the original plan was to be able to replace the modular circuits. Then Wal-Mart (Mall-Wart?) changed suppliers... and presto, I had to make up a new 2-channel Instructable instead of repeating one of these 8-channel modules. Moral: buy your spares when you buy your original circuits.

Step 28: Controller Interface Circuit

With the Basic Stamp Homework board I could drive 16 channels directly, but that would be the maximum. When I went from 8 to 16 channels I decided to use octal latches to hold each modules' control bits and address each one with a direct control line. With this simple control scheme the Stamp can drive 8 sets of 8 bits, or up to 64 channels. I'll never need that many, but someone might. If you want more than that you must use address decoders.

The program sets the control bits on pins 0-7, then writes a 1, 0 sequence to the control pin (8 or 9 or both) to latch the control bits. The modules are connected to the control board with 9-pin connectors so the board can be easily removed for programming. The octal latches are 74573 types; any version will do, this is not a high speed circuit.

Step 29: Controller Board

The interface circuit is built on a little strip of perfboard and the wires are run into the connectors on the Homework board. Built quickly, it's not pretty but it works. The controller is held in the weather enclosure box with thumb screws so it can easily be taken out for programming. Also shown here are test plugs so the software can be tested separate from the rest of the hardware (which is outside). The test plugs are a duplicate of the LED circuits in the modules, using 10-bar LED displays from Radio Shack and all the resistors crammed into the connector headshells. The only weirdness is that the LEDs are on when the controlled lamps are off, so you see the "inverted" display when testing, but that suffices.

Step 30: Enclosure

The controller is outside, so it has to be protected. This box came with an outdoor game that the kids have lost all the pieces for. I cut a hole in the bottom for all the extension cords to run out and added some legs. The box has four yellow latches that hold the lid on, making it very easy to open. The inside was shown back in step 23 with the controller bolted in.

To use this system, each light string is connected to its outlet with a 9 foot extension cord. The 32 mini-light strings were about $3 each, the maypole is just an extension pole for painting with a hook fixture added to the top, and the extension cords are about $3 apiece. So the whole setup is very inexpensive.

Referring to the picture in step 24, the light strings are tied together in a V for the top strings and an upside down W for the bottom ones. Strings opposite each other around the circle are the same color. The control possibilities are endless, or at least they would be if you could fit the routines into this little microcontroller's memory.

Step 31: Controller Program

I conclude this appendix with the code for the Basic Stamp. It's commented so I won't say much about it here, it's only included as an example. The code was written before this Instructable, so the comments are a bit repetitive assuming you've read everything up to this point. Good luck with your own project.


' {$STAMP BS2}
' {$PBASIC 2.5}

'Version of the "Westinghouse" Xmas tree driver for the 16 light-string tree.
'This is an homage to the original tree used at the Westinghouse R&D center in Churchill, PA.
'Runs 2 modules of 8 110V outlet controllers, 1 for 8 upper strings, 1 for 8 lower strings
'Each module driven by TTL 74-573 series octal latches. Data from the lower IO byte is
'written/held by low-high-low transitions on control bits from the upper IO byte.
'Module 1 (upper lights) latched by port 8 control bit, module 2 (lower) by port 9 bit.
'The 2 modules as used here are expandable to 8 modules for total of 64 controlled outlets

' This version minimizes memory usage by eliminating the randomized variables
' and also the structured design of having a main loop that calls lighting
' routines as subroutines. Now all routines are in the main loop itself.
' Yes, bad for having less structure but saves many bytes. Several of the
' earlier routines were also rewritten to use more loops since they take up
' much less space than just stepping through lists of lamp values. Relying
' more on loops has the downside of constraining the patterns a little, but
' makes the programming more intricate and interesting.

'To download program - connect COM1 serial cable to powered-up board
'press Play button on the tool bar

'To test individual routines paste routine into "xmaslites2-minimized-testfile.bs2"
'REMEMBER to update the test file if variable definitions or subroutines change

'----------------------------------------------------------------------------

'define and set variables
ctr1 VAR Nib 'counter indices for main routine loops
ctr2 VAR Nib
ctr3 VAR Nib

subctr1 VAR Nib 'counter index for subroutine loops
subpause VAR Word 'variable to pass pause time to subroutines
shiftcount VAR Nib 'counter for the number of shifts

lpctr1 VAR Nib 'counter lengths for main routine loops
lpctr2 VAR Nib
delay1 VAR Word 'more-or-less constants for pauses in routines
delay2 VAR Word

lowerlamps VAR Byte 'holds lamp state bits for lower 8 strings
upperlamps VAR Byte 'holds lamp state bits for upper 8 strings

holdvar1 VAR Byte 'temp lamp value holder
holdvar2 VAR Byte 'temp lamp value holder
shiftee VAR Byte 'temp variable for shifting routines

OUTL = %00000000 'set all data lines low
OUTH = %00000000 'set all command lines low
DIRS = %1111111111111111 'set all I/O pins to output mode

'Set values for the constants that set the overall look and speed of the tree
delay1 = 500 'half-second delay
delay2 = 800
lpctr1 = 3
lpctr2 = 6

'main loop - in this minimal memory version, the lamp routines are not called as subroutines
DO

'MMM AllFlash MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

FOR ctr1 = 1 TO 4
OUTL = %11111111 'all lights on
GOSUB Drivemodule12
PAUSE delay2
OUTL = %00000000 'all lights off
GOSUB Drivemodule12
PAUSE delay2
NEXT

'NNN Sequence and Fill NNNNNNNNNNNNNNNNNNNNNNNN
'This routine circulates a lighted string around, leaving behind
'an additional lit string each time until all the strings are lit.
'Starts with the bottom circle of strings.

holdvar1 = %00000000 'This variable accumulates the strings left lit on
'the MSB end of the set - starting with the MSB string
'then continuing by adding MSB-1, MSB-2, etc. The
'MSB string is string -H- on the light string labels
ctr3 = 8 'This counter is decremented as the number of passes needed
'to push a lighted string onto the accumulated pile decreases
'with each added string
FOR ctr1 = 1 TO 8
shiftee = %00000001 'light string control bit starts at LSB
FOR ctr2 = 1 TO ctr3
lowerlamps = holdvar1 | shiftee 'already lit strings ORd with the shifting string
OUTL = lowerlamps 'result of OR is sent to the control module
GOSUB Drivemodule2
PAUSE delay1
shiftee = shiftee << 1 'shift the lighted string to the left 1 place
NEXT
holdvar1 = lowerlamps 'remember final lighted string positions as each inner loop
'adds one more lighted string onto the MSB end
ctr3 = ctr3-1
NEXT

holdvar1 = %00000000 'same routine as above, filling in the upper strings
ctr3 = 8 'while the lower strings all remain lit
FOR ctr1 = 1 TO 8
shiftee = %00000001
FOR ctr2 = 1 TO ctr3
upperlamps = holdvar1 | shiftee
OUTL = upperlamps
GOSUB Drivemodule1
PAUSE delay1
shiftee = shiftee << 1
NEXT
holdvar1 = upperlamps
ctr3 = ctr3-1
NEXT 'All lamps left on

'NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

'QQQQ Over the Top QQQQQQQQQQQQQQQQQQQQQQQQQQQ
'Similar to Sequence and Fill, above, except the lights circulate over the top
'of the tree on pairs of opposite strings (same color strings), leaving behind
'an additional lit string each time until all the strings of the pair are lit.

holdvar1 = %00000000 'This variable accumulates lower strings left lit
holdvar2 = %00000000 'This variable accumulates upper strings left lit

'NOTE: this routine is not finished yet

'QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ

'VVVVV Drain Around VVVVVVVVVVVVVVVVVVVV
'Light all lamps then turn off sequentially top/bot
'once around the tree until all strings are off
'note that OUTL holds the current state of the tree

OUTL = %11111111 'start with all lights on
GOSUB Drivemodule12
PAUSE delay2

FOR ctr2=1 TO 8
OUTL = OUTL >> 1 'right shift, shifts in zeroes
GOSUB Drivemodule1 'turn off upper string
PAUSE delay2
GOSUB Drivemodule2 'turn off lower string
PAUSE delay2
NEXT

'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV

'WWWWWW Top Bottom Flash WWWWWWWWWWWWWWWWWWWW
'flash all bottom and top strings back and forth

FOR ctr2=0 TO lpctr2
OUTL = %11111111
GOSUB Drivemodule1 'all top strings on
OUTL = %00000000
GOSUB Drivemodule2 'all bottom strings off
PAUSE delay1 'wait
OUTL = %11111111
GOSUB Drivemodule2 'all bottom strings on
OUTL = %00000000
GOSUB Drivemodule1 'all top strings off
PAUSE delay1 'wait

NEXT
GOSUB Drivemodule12 'Ends with all lights off

'WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW

'CCC SeqOnOff Subroutine CCCCCCCCCCCCCCCCCCCCCC
'Lights all strings sequentially, then off sequentially
'with increasing/decreasing pauses
'top and bot controlled together

OUTL = %00000000 'start by setting all lights off
GOSUB Drivemodule12
PAUSE delay2

FOR ctr2=0 TO lpctr1

'lights on sequentially CW one top/bot string at a time
FOR ctr1=0 TO 7
HIGH ctr1
GOSUB Drivemodule12
PAUSE delay2-(ctr2*(delay1-100)/2)
NEXT

'lights off sequentially CW top/bot string at a time
FOR ctr1=0 TO 7
LOW ctr1
GOSUB Drivemodule12
PAUSE 100+(ctr2*(delay1-100)/2)
NEXT

NEXT 'Ends with all lights off

'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

'JJJ Cylon JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
'front 5 strings - bounce lit string back and forth fast
'top string only ... for now
'Must be started with all lights off

upperlamps = %00000010
subpause = 200
shiftcount = 4

FOR ctr1 = 0 TO lpctr2

GOSUB ShiftLeftUpper
GOSUB ShiftRightUpper

NEXT

OUTL = %00000000 'all lights off
GOSUB Drivemodule12

'JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ

'HHH SeqOnOff Opposites Subroutine HHHHHHHHHHHHH
'Lights all strings sequentially, then off sequentially
'with increasing/decreasing pauses
'top and bot in opposite directions

' THIS ROUTINE NOT WRITTEN YET, STILL IDENTICAL TO SeqOnOff ABOVE

'FOR ctr2=0 TO lpctr1

'lights on CW sequentially one string at a time
'FOR ctr1=0 TO 7
' HIGH ctr1
' GOSUB Drivemodule12
' PAUSE delay2-(ctr2*(delay1-100)/2)
'NEXT

'lights off sequentially CW one string at a time
'FOR ctr1=0 TO 7
' LOW ctr1
' GOSUB Drivemodule12
' PAUSE 100+(ctr2*(delay1-100)/2)
'NEXT

'NEXT 'Ends with all lights off

'HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

'AAA Circulate1 Subroutine AAAAAAAAAAAAAAAAAAAAAAA
'3 lit strings chase around, increasing speed each repeat
'circulates 4 times, repeats random lpctr2 times
'top and bottom are in phase

'Circulate1:

FOR ctr2=0 TO lpctr2

OUTL = %00000001 'light 1,2,3 strings top & bot before entering chasing loop
GOSUB Drivemodule12
PAUSE 150-(ctr2*15)

OUTL = %00000011
GOSUB Drivemodule12
PAUSE 150-(ctr2*15)

lowerlamps = %00000111 'lowerlamps variable used for lower and upper together
subpause = 150-(ctr2*15)
shiftcount = 9
GOSUB ShiftLeftBoth

NEXT 'Ends with all lights off

'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

'PPP GirlsGirlsGirls Alternate Sparkly Subroutine PPPPPPPPPP
'flashes between adjacent strings, upper and lower alternating
'Sparkling occurs when string on time is short, around 100

'GirlsGirlsGirls:

FOR ctr2=0 TO 16
OUTL = %01010101
GOSUB Drivemodule1
OUTL = %10101010
GOSUB Drivemodule2
PAUSE 100

OUTL = %10101010
GOSUB Drivemodule1
OUTL = %01010101
GOSUB Drivemodule2
PAUSE 100

NEXT 'Ends with lights in last postition

'PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP

'DDD RotatePairs Subroutine DDDDDDDDDDDDDDDDDDD
'rotating pairs of strings opposite of each other

'RotatePairs:
OUTL = %00000000 'clear all lights to off

FOR ctr2=0 TO lpctr2
FOR ctr1=0 TO 3
HIGH ctr1
HIGH ctr1+4
GOSUB Drivemodule12
PAUSE delay1
LOW ctr1
LOW ctr1+4
GOSUB Drivemodule12
NEXT
NEXT 'Ends with lights in last postition

'DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD

'EEE CounterRotate EEEEEEEEEEEEEEEEEEEEEEEEEEE
'counterrotating pairs
'CounterRotate:

FOR ctr2=0 TO lpctr1

OUTL = %10000000
GOSUB Drivemodule12
PAUSE delay1
OUTL = %01000001
GOSUB Drivemodule12
PAUSE delay1
OUTL = %00100010
GOSUB Drivemodule12
PAUSE delay1
OUTL = %00010100
GOSUB Drivemodule12
PAUSE delay1
OUTL = %00001000
GOSUB Drivemodule12
PAUSE delay1
OUTL = %00010100
GOSUB Drivemodule12
PAUSE delay1
OUTL = %00100010
GOSUB Drivemodule12
PAUSE delay1
OUTL = %01000001
GOSUB Drivemodule12
PAUSE delay1

NEXT 'Ends with lights in last postition

'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE

OUTL = %00000000 'all lights off
GOSUB Drivemodule12
PAUSE delay1

'FFF Star Sparkle Subroutine FFFFFFFFFFFFFFFFFFFFFFF
'star pattern A-F-C-H-E-B-G-D, bottom lags top by 1 cycle
'Sparkles due to short on time pause of 100
'Old version of Star had a delay that changed with each loop
'with this formula - delay1-(ctr2*(delay1-100)/lpctr1)
'Star:

FOR ctr2=0 TO lpctr2
OUTL = %00000001
GOSUB Drivemodule1
PAUSE 100
OUTL = %00100000
GOSUB Drivemodule1
PAUSE 100
OUTL = %00000001
GOSUB Drivemodule2
PAUSE 100
OUTL = %00000100
GOSUB Drivemodule1
PAUSE 100
OUTL = %00100000
GOSUB Drivemodule2
PAUSE 100
OUTL = %10000000
GOSUB Drivemodule1
PAUSE 100
OUTL = %00000100
GOSUB Drivemodule2
PAUSE 100
OUTL = %00010000
GOSUB Drivemodule1
PAUSE 100
OUTL = %10000000
GOSUB Drivemodule2
PAUSE 100
OUTL = %00000010
GOSUB Drivemodule1
PAUSE 100
OUTL = %00010000
GOSUB Drivemodule2
PAUSE 100
OUTL = %01000000
GOSUB Drivemodule1
PAUSE 100
OUTL = %00000010
GOSUB Drivemodule2
PAUSE 100
OUTL = %00001000
GOSUB Drivemodule1
PAUSE 100
OUTL = %01000000
GOSUB Drivemodule2
PAUSE 100
OUTL = %00000000
GOSUB Drivemodule1
PAUSE 100
OUTL = %00001000
GOSUB Drivemodule2
PAUSE 100
OUTL = %00000000
GOSUB Drivemodule2
PAUSE 100

NEXT 'Ends with light off
PAUSE 1500

'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

'BBB Circulate2 Subroutine BBBBBBBBBBBBBBBBBBBBBBBBB
'3 lit strings chase around, increasing speed each repeat
'circulates 4 times, lpctr1 times
'top and bottom are 90 degrees out of phase
'reminder - module1 is upper lamps, module 2 is lower ones
'Circulate2:

FOR ctr2=0 TO lpctr1

OUTL = %00000001 'light 2 strings top & bot before entering chasing loop
GOSUB Drivemodule1
PAUSE 150-(ctr2*15)

OUTL = %00000011
GOSUB Drivemodule1
PAUSE 150-(ctr2*15)

OUTL = %00000111
GOSUB Drivemodule1
OUTL = %00000001
GOSUB Drivemodule2
PAUSE 150-(ctr2*15)

OUTL = %00001110
GOSUB Drivemodule1
OUTL = %00000011
GOSUB Drivemodule2
PAUSE 150-(ctr2*15)

OUTL = %00011100
GOSUB Drivemodule1
OUTL = %00000111
GOSUB Drivemodule2
PAUSE 150-(ctr2*15)

upperlamps = %00011100
lowerlamps = %00000111

FOR ctr1=1 TO 8 'shift 3 lit strings around 1 full revolution until all off

upperlamps = upperlamps << 1
lowerlamps = lowerlamps << 1
OUTL = upperlamps
GOSUB Drivemodule1
OUTL = lowerlamps
GOSUB Drivemodule2
PAUSE 150-(ctr2*15)

NEXT

NEXT 'Ends with all lights off

'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

'GGG GirlsGirlsGirls Alternate Subroutine GGGGGGGGGGGGGGGG
'flashes between adjacent strings, upper and lower alternating

'GirlsGirlsGirls:

FOR ctr2=0 TO lpctr2
OUTL = %01010101
GOSUB Drivemodule1
OUTL = %10101010
GOSUB Drivemodule2
PAUSE delay1

OUTL = %10101010
GOSUB Drivemodule1
OUTL = %01010101
GOSUB Drivemodule2
PAUSE delay1

NEXT 'Ends with lights in last postition

'GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG

OUTL = %00000000 'all lights off
GOSUB Drivemodule12
PAUSE delay1

'MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

'III ChasePair Subroutine IIIIIIIIIIIIIIIIIIII
'One pair separated by 1 blank rotating CCW

'ChasePair:

FOR ctr2=0 TO lpctr2

lowerlamps = %10100000
shiftcount = 6
subpause = delay2-(ctr2*(delay1-100)/lpctr1)
GOSUB ShiftRightBoth 'use subroutine to shift pattern to far right

OUTL = %10000010 'manually do this light position
GOSUB Drivemodule12 'because shift fills with zeroes
PAUSE delay2-(ctr2*(delay1-100)/lpctr1)

lowerlamps = %01000001
shiftcount = 1
GOSUB ShiftRightBoth

NEXT 'Ends with lights in last postition

'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII

'KKK FlashQtrs Subroutine KKKKKKKKKKKKKKKKKKKKK
'flashes Qtrs of tree: red-green and white-blue

'FlashQtrs:

FOR ctr2=0 TO lpctr2

OUTL = %00110011
GOSUB Drivemodule12
PAUSE delay2

OUTL = %11001100
GOSUB Drivemodule12
PAUSE delay2

NEXT 'Ends with lights in last postition

'KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK

'LLL GirlsGirlsGirls Subroutine LLLLLLLLLLLLLLL
'flashes between adjacent strings

'GirlsGirlsGirls:

FOR ctr2=0 TO lpctr2
OUTL = %01010101
GOSUB Drivemodule12
PAUSE delay1

OUTL = %10101010
GOSUB Drivemodule12
PAUSE delay1

NEXT 'Ends with lights in last postition

'LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL

LOOP 'Bottom of main loop

'----Subroutines -------------------------------------

'The Drivemodule subroutine writes the data byte appearing on ports 0-7
'to the module address or addresses called out in the subroutine name.
'For the 74LS573 latches this means driving the selected control lines
'from low to high then driving all the control lines to low again.
'The control bits will be all low when entering and leaving this routine.
'Port 8 controls module 1, port 9 controls module 2, etc, so this style
'of module addressing can be expanded to 64 individual light strings.
'control byte for module 1 - %00000001
'control byte for module 2 - %00000010
'control byte for modules 1 and 2 - %00000011

'Module 1 controls the upper 8 light strings, module 2 the lower ones
'Note that multiple modules can be addressed at the same time, although
'each module will get the same light control bits from ports 0-7 that way.

'Future development note - the current programming style using separate
'subroutines for each combination of modules uses much less space than
'defining a control variable and calling a single routine, but this style
'may get cumbersome for 3 or more modules and combinations thereof.

Drivemodule1:
OUTH = %00000001
OUTH = %00000000
RETURN

Drivemodule2:
OUTH = %00000010
OUTH = %00000000
RETURN

Drivemodule12:
OUTH = %00000011
OUTH = %00000000
RETURN

'The ShftX8X subroutines take in a byte of lamp values, displays them then
'shifts them, doing this <shiftcount> times.
'The first four subroutines handle left and right shift of upper and lower lamps.
'The last two shift both upper and lower 8 bits, using the lowerlamps variable.
'Pass in variables Subctr1, shiftcount, subpause, and lower- or upperlamps;
'changes value of OUTL and lower- or upperlamps.

'NOTE - the two -shiftlower- routines have not been used yet
'As with the module driving routine, it still takes up less memory to have
'individual routines for each possible shift operations rather than using
'a more universal set of routines Whne ther is a control variable designed
'for the module driving routine, then using that routine will eliminate
'the need for duplicate shifting routines.

ShiftLeftUpper:
FOR subctr1 = 1 TO shiftcount
OUTL = upperlamps
GOSUB Drivemodule1
PAUSE subpause
upperlamps = upperlamps << 1
NEXT
RETURN

'ShiftLeftLower:
'FOR subctr1 = 1 TO shiftcount
' OUTL = lowerlamps
' GOSUB Drivemodule2
' PAUSE subpause
' lowerlamps = lowerlamps << 1
'NEXT
'RETURN

ShiftRightUpper:
FOR subctr1 = 1 TO shiftcount
OUTL = upperlamps
GOSUB Drivemodule1
PAUSE subpause
upperlamps = upperlamps >> 1
NEXT
RETURN

'ShiftRightLower:
'FOR subctr1 = 1 TO shiftcount
' OUTL = lowerlamps
' GOSUB Drivemodule2
' PAUSE subpause
' lowerlamps = lowerlamps >> 1
'NEXT
'RETURN

ShiftLeftBoth:
FOR subctr1 = 1 TO shiftcount
OUTL = lowerlamps
GOSUB Drivemodule12
PAUSE subpause
lowerlamps = lowerlamps << 1
NEXT
RETURN

ShiftRightBoth:
FOR subctr1 = 1 TO shiftcount
OUTL = lowerlamps
GOSUB Drivemodule12
PAUSE subpause
lowerlamps = lowerlamps >> 1
NEXT
RETURN