Friday, December 23, 2016

Christmas Lights - Final Technical Details

Since I've already posted here a short article about the completion of this project weeks ago, I figured it was probably time to provide all the details.

First, the schematic:



As I've already written about, the transistors I've been using are IRLML2502 which are rated at several amps each (assuming you don't let me get too hot) which is more than sufficient for each channel.

I've included a passably-accurate DS1307 RTC to allow me to define the starting and stopping time for the lights each day. It does drift, somewhere on the order of seconds a day and there is a fancy version of the driver for this IC that would allow me to custom-calibrate the device. For this purpose, not worth the effort.

Lastly, as you can see, I'm using a Mega and the reason is that, with a little bit of extra configuration code, you can enable several of the timers in 16-bit mode. This provides a greater number of steps when controlling the dimming of the lights with PWM. 

And now for the code....


 /**********************************************  
  * Written by Trevor Hardy 2016  
  *********************************************/  
 #include <PWM.h>  
 #include <Wire.h>  
 #include <Time.h>  
 #include <DS1307RTC.h>  
 /*******************************************************************  
 For LEDs, linear changes in brightness are not linear in value.  
 These constants below provide a linear increase in LED brightness  
 The brightness array below is zero-indexed. For example:  
 LED_brightness[0] = 0  
 LED_brightness[5] = 6  
 LED_brightness[33] = 61000  
 LED_brightness[34] = invalid  
 *******************************************************************/  
 const uint16_t linear16[33] = {0, 1, 2, 3, 4, 6, 8, 11, 16, 23, 32, 45, 64, 91, 128, 181, 256,\  
                362, 512, 724, 1024, 1448, 2048, 2896, 4096, 5792, 8192, 11585,\  
                16384, 23170, 32768, 46340, 61000};  
 //Pinouts of the three LED strips  
 const int s1Red = 44;  //Timer 3 channel A    
 const int s1Green = 46; //Timer 3 channel C    
 const int s1Blue = 45;  //Timer 3 channel B   
 const int s2Red = 8;  //Timer 4 channel C  
 const int s2Green = 7; //Timer 4 channel B    
 const int s2Blue = 6;  //Timer 4 channel A    
 const int s3Red = 5;  //Timer 5 channel A  
 const int s3Green = 3; //Timer 5 channel B    
 const int s3Blue = 2; //Timer 5 channel C  
 //Delays   
 const int8_t step_delay = 75; //Time at each PWM step, ms  
 const unsigned int color_hold_time = 1000*3; //Time between fades, hold time at a given color.  
 const int32_t PWM_frequency = 131; //Frequency in Hz.  
 //Adjustment values used to mke the blue a little warmer  
 int blue_idx = 0; //Redefined inside each fade loop  
 const int blue_shift = 4; //Number of PWM steps down from the true max. blue_shift = 6 -> max PWM step = 8192  
 //Start and stop time  
 const int start_hour = 16;  
 const int start_minute = 00;  
 const int stop_hour = 7;  
 const int stop_minute = 30;  
 int run_lights = 0;  
 //Time-keeping variable  
 tmElements_t tm;  
 void setup() {  
  //initialize all timers except for 0, to save time keeping functions. Allow for 16-bit PWM natively  
  InitTimersSafe();   
  /*  
  Initializing freqency for all timers. Each timer affects all three channels (colors)  
  so only need to initilize one channel per group.  
  */  
  //Blinking to indicate start of initialization  
  pinMode(13, OUTPUT);  
  digitalWrite(13, LOW);   
  delay(1000);  
  for (int idx = 0; idx < 2; idx++){  
   digitalWrite(13, HIGH);   
   delay(400);  
   digitalWrite(13, LOW);  
   delay(400);  
  }  
  delay(1000);  
  set_frequency(s1Red, PWM_frequency);   
  set_frequency(s2Red, PWM_frequency);  
  set_frequency(s3Red, PWM_frequency);  
  Serial.begin(9600);  
  pwmWriteHR(s1Red, 0);  
  pwmWriteHR(s1Green, 0);  
  pwmWriteHR(s1Blue, 0);  
  pwmWriteHR(s2Red, 0);  
  pwmWriteHR(s2Green, 0);  
  pwmWriteHR(s2Blue, 0);  
  pwmWriteHR(s3Red, 0);  
  pwmWriteHR(s3Green, 0);  
  pwmWriteHR(s3Blue, 0);  
 }  
 void loop() {  
  if (RTC.read(tm)) {  
   Serial.print("Ok, Time = ");  
   print2digits(tm.Hour);  
   Serial.write(':');  
   print2digits(tm.Minute);  
   Serial.write(':');  
   print2digits(tm.Second);  
   Serial.print(", Date (D/M/Y) = ");  
   Serial.print(tm.Day);  
   Serial.write('/');  
   Serial.print(tm.Month);  
   Serial.write('/');  
   Serial.print(tmYearToCalendar(tm.Year));  
   Serial.println();  
  } else {  
   if (RTC.chipPresent()) {  
    Serial.println("The DS1307 is stopped. Please run the SetTime");  
    Serial.println("example to initialize the time and begin running.");  
    Serial.println();  
   } else {  
    Serial.println("DS1307 read error! Please check the circuitry.");  
    Serial.println();  
   }  
   delay(9000);  
  }  
  delay(1000);  
  if ((tm.Hour == start_hour && tm.Minute >= start_minute) || (tm.Hour > start_hour) ||( tm.Hour < stop_hour) || (tm.Hour == stop_hour && tm.Minute < stop_minute)){
      run_lights = 1;
  }else if ((tm.Hour == stop_hour && tm.Minute >= stop_minute) || (tm.Hour > stop_hour) || (tm.Hour < start_hour) || (tm.Hour == start_hour && tm.Minute < start_minute)){
      run_lights = 0;
  }
  Serial.print("run_lights: \t");  
  Serial.println(run_lights);    
  if (run_lights == 1){    
  //Christmas ramps  
  for(int idx=0; idx<=32; idx++){  
   blue_idx = max(0,idx-blue_shift);  
   //String 1 - Red to green  
   pwmWriteHR(s1Red, linear16[32-idx]);  
   pwmWriteHR(s1Green, linear16[idx]);  
   //String 2 - Green to white  
   pwmWriteHR(s2Red, linear16[idx]);  
   pwmWriteHR(s2Green, linear16[max(32-idx, idx)]);  
   pwmWriteHR(s2Blue, linear16[blue_idx]);  
   //String 3 - White to red  
   pwmWriteHR(s3Red, linear16[max(32-idx,idx)]);  
   pwmWriteHR(s3Green, linear16[32-idx]);  
   pwmWriteHR(s3Blue, linear16[32-blue_shift-blue_idx]);  
   delay(step_delay);  
  }  
  delay(color_hold_time);  
  for(int idx=0; idx<=32; idx++){  
   blue_idx = max(0,idx-blue_shift);  
   //String 1 - Green to white  
   pwmWriteHR(s1Red, linear16[idx]);  
   pwmWriteHR(s1Green, linear16[max(32-idx, idx)]);  
   pwmWriteHR(s1Blue, linear16[blue_idx]);  
   //String 2 - White to red  
   pwmWriteHR(s2Red, linear16[max(32-idx,idx)]);  
   pwmWriteHR(s2Green, linear16[32-idx]);  
   pwmWriteHR(s2Blue, linear16[32-blue_shift-blue_idx]);  
   //String 3 - Red to green  
   pwmWriteHR(s3Red, linear16[32-idx]);  
   pwmWriteHR(s3Green, linear16[idx]);  
   delay(step_delay);  
  }   
  delay(color_hold_time);  
  for(int idx=0; idx<=32; idx++){  
   blue_idx = max(0,idx-blue_shift);  
   //String 1 - White to red  
   pwmWriteHR(s1Red, linear16[max(32-idx,idx)]);  
   pwmWriteHR(s1Green, linear16[32-idx]);  
   pwmWriteHR(s1Blue, linear16[32-blue_shift-blue_idx]);  
   //String 2 - Red to green  
   pwmWriteHR(s2Red, linear16[32-idx]);  
   pwmWriteHR(s2Green, linear16[idx]);  
   //String 3 - Green to white  
   pwmWriteHR(s3Red, linear16[idx]);  
   pwmWriteHR(s3Green, linear16[max(32-idx, idx)]);  
   pwmWriteHR(s3Blue, linear16[blue_idx]);  
   delay(step_delay);  
  }  
  delay(color_hold_time);  
  }  
  else{  
   pwmWriteHR(s1Red, 0);  
   pwmWriteHR(s1Green, 0);  
   pwmWriteHR(s1Blue, 0);  
   pwmWriteHR(s2Red, 0);  
   pwmWriteHR(s2Green, 0);  
   pwmWriteHR(s2Blue, 0);  
   pwmWriteHR(s3Red, 0);  
   pwmWriteHR(s3Green, 0);  
   pwmWriteHR(s3Blue, 0);  
  }  
 }  
 //------------- Functions ----------------------  
 //Only used to initialize PWM timers  
 void set_frequency(int pin, int frequency){  
  bool success = SetPinFrequencySafe(pin, frequency);  
  //Use LED to indicate if successfully changed timer frequency  
  if(success) {  
   digitalWrite(13, HIGH);  
   delay(50);  
   digitalWrite(13, LOW);    
   delay(250);  
  }  
 }  
 void print2digits(int number) {  
  if (number >= 0 && number < 10) {  
   Serial.write('0');  
  }  
  Serial.print(number);  
 }  

There is no glory in this code but it does work. As you can see the ramps are manually defined rather than through some cool function; maybe next year if we decide to do something more complicated. The visually linear stepping of the PWM follows is proportional to the root of two. 

Though moving from 8 bit 16 bits does dramatically increase the number of steps, these extra bits only expand the low end of the dynamic range. My initial implementation was just using 8-bits and there was a clear need for dimmer values, thus the extra effort to turn on the 16-bit timers. The Uno has one or two 16-bit timers that can be enabled but I needed nine of them, thus the Mega (or the extra hassle of an external IC).

I still observe some steppy-ness at the brighter values and may end up adding in some extra steps along the curve for smoother transitions. This is just a matter of interpolating along the root-two curve and I've started very preliminary work on it already. At very low values interpolation is not possible as the required PWM values are already low-valued integers; the first five values are 0, 1, 2, 3, 4 which clearly do not follow a root-two progression. Thankfully, there's plenty of integers available at the bright end of the scale.  Adding in extra values will introduce some non-linearity in the fade but I'm hoping it won't be too noticeable.

All three of the windows this year are being controlled by a single Mega. If I was to expand to other windows and wished to coordinate them, it would require coordination among the Mega's in some form. Getting a high-accuracy clock with very low drift might be enough (a cheap GPS clock would be sufficient) or using a simple radio-based protocol would do the trick as well. Either way, new hardware would be required; we'll see what my wife and I feel is appropriate.

Sunday, December 18, 2016

Christmas Lights - Power Consumption

I've been running my mulit-colored Christmas lights (as shown here) for a few weeks. Living above the 45th parallel being in the winters, it gets dark early. I've set the lights to turn on at 4pm and run all night, turning off at 7am. I've been using a Kill-a-Watt to measure the total power consumption and here's where we are, a week before Christmas.

...And I just remembered we lost power yesterday so the Kill-a-Watt reset. So, uhmm, after 24 hours of operation the lights consumed 0.31 kWh.

Assuming I run this for most of December (we'll say 30 days), this means the total energy consumption is 9.3 kWh. At $0.06/kWh, the total operational cost of running the lights is $0.56. 

This my frugality can handle.

Saturday, December 03, 2016

Christmas Decorations Accomplished

The first weekend after getting back from our trip to see family in Oklahoma, we managed to more or less do our decorating for Christmas. At least the major parts. How did we do this in one day?  Well, we don't do a lot of decorating. Mostly a few lights and a tree.  But its done and here's how it looks:


The color-changing lights are the finished (but not fully documented here, at least not yet) project I've started writing about. Nerd grade: 3; its a start and with my wife's consent we will be adding more windows next year.

The tree was purchased and decorated this morning.  I plan on making a more formal version of my automated Christmas tree water-er rather than the bread-board mess I whipped together last time. Not only does this increase the nerd score for Christmas, it saves the hassle of crawling and pouring water into the tree stand; my wife likes this idea very much.

The snowflake and leaf lights are what we've done the past two years at this house. They were our token effort to do external lighting.

There's probably going to be incremental decorating on the interior of the house but our public-facing portion is complete. Not bad for a days work.

Saturday, November 05, 2016

Christmas Lights - SMT Soldering

Last night was a night of working on small things: my wife continued her efforts on a counted cross-stitch stocking for me and I soldered some very small transistors to break-out boards.

Specifically, I soldered two IRLML2502s to a 10-pin SOT23 break-out board. I knew these transistors were small and I had figured out a way to squeeze two of them onto the break-out boards. It took me about an hour to make five the modules (one of which you see below) and I haven't electrically verified that all the connections are made properly so there may be a little bit of rework.


So, yeah, small. The horizontal dimension of one of the transistors is between 2.67 and 3.05 mm, the vertical, counting the leads, is between 2.1 and 2.5 mm.

My original plan was to solder these directly to 0.1" perf board that makes up most of the custom Arduino shield I'm making. I think that would still work with a little bit of fudging but when I realized I could get two transistors on one break-out board, I decided the extra hassle was worth having the transistors easily replaceable.

Because, oddly, there is no nominal current rating for these transistors, only an absolute maximum rating (4.2A continuous at 25'C, 3.4A at 70'C). I won't be needing more than 1A per channel so I feel safe using them but it feels a little bit like dangerous or rebellious engineering to use them not knowing what a normal current rating is. How lucky do you feel? How hot will they get? Will it work or will it end in the magic smoke being released? Having them on a module means I can easily replace a pair if something goes wrong.

You might notice that the second pin in from the upper right is missing. This is intentional and a result of my experiences on other projects. One of the most common problems I face with symmetrical connections is trying to figure out which way I should plug things in. Plugging in the wrong way can blow things up but I rarely want to take the time to go back to a schematic to figureit out. By leaving the pin empty I can put a dab of hot glue over the corresponding hole in the connector and effectively prevent myself from being able to plug these in backwards.

Oh, and in case you missed the title, I'm working on some decorative LED Christmas lights using the ever popular RGB LED strips. More to come, hopefully very soon.

Monday, October 24, 2016

Backyard Landscaping - Raised Garden Bed

My wife is committing to growing fruits, vegetables, and herbs.

At least that's how I see it after completing a sixteen by four by two foot raised garden bed. The project included borrowing a chop-saw from a friend, a slightly treacherous and probably ill-advised trip home from the hardware store with sixteen-foot pieces of lumber strapped to the roof of the car, and much more painting than I would have anticipated.

Oh, and digging in the dirt. Lots of digging.


So here is the box, assembled and upside down. The black boarder around the bottom is my attempt at keeping the Bermuda grass from growing up through the bottom of the box, just like I did with the basketball court removal. As before, we'll see if it does any good.


After a bit of digging, the grass was out and I began the process of digging out the holes for the feet. What I hadn't discovered yet is that there is a sprinkler pipe up near the foot of the stairs that is going to cause a bit of trouble. I had to shift the bed away from the patio concrete enough to clear that pipe.


With my wife's help, we managed to flip it over easily enough. And then I began the level process, digging around the footings, measuring, propping up and pressing down until it was more or less level. You can see a little dab of the white PVC pipe that made this just a bit more tricky.


The last step was moving all the dirt from the patio to the bed and kind of leveling it out. As you can see, the bed is only about a third full so before we plant in the spring we are going to need to get some more.

Saturday, September 17, 2016

Backyard Landscaping - Grass Almost There


Looking pretty good, eh?  Looking at the last photo from about six weeks ago, its starting to look like a real lawn; I even mowed for the first time this morning. Even without zooming in, you can see some bare spots and I re-seeded two weeks ago to try to fill those in. When I look closely, I can seem some of those seeds have sprouted but not near as many as I had hoped. I'll probably have to seed and fertilize again in the spring. At least we won't have a mud pit for the summer.

And there's more to my life than this lawn, I promise. Hopefully some of that will show up here soon.

Tuesday, August 23, 2016

Backyard Landscaping - Growing Grass Still


I don't know what to say other than this blog, as of late, is literally as exciting as watching grass grow.

And it will continue to be that way as it is clear from the photo above that I will need to add some seed in the coming weeks. The grass I planted doesn't grow super well in the heat and its just now cooling off from the summer. I'm hoping the second seeding will fill things in well enough that we can avoid mud for the winter.

Wednesday, August 03, 2016

Backyard Landscaping - Growing Grass

I know, I know. So many of you have been asking, "Whatever happened to that grass you planted?"
Here's a not-very-good picture taken after the sun had gone down this evening of this little guys getting going. I have no idea why they are popping up in such a patchy manner; I might have to re-seed in the fall before the water is turned off.


Sunday, July 24, 2016

Backyard Landscaping - Dirt In and Grass Planted

While I was out of town this past week, my sister and her husband came to help visit and assist my wife with all the running of the house and caring for our son. And while they were at it, they moved the rest of the dirt in. Last night I planted the grass and tonight, when our irrigation water returned, I started watering. Let's hope our recently spotty irrigation water holds up for a week or two so the seed can germinate.



The lighter spot in the lower left is not covered by the sprinkler head very well. Though they claim to spread the water uniformly, that is clearly not the case.  I'll be able to cover it decently with water from another zone.

Sunday, July 17, 2016

Backyard Landscaping - Smoothing the Fill Dirt

The dirt is definitely smoother but this photo is deceiving. There's still much more to move in and a lot of smoothing still to go.


Newberry National Volcanic Monument

So, before we begin with the pictures, the difference between a National Park and a National Monument in the United States has only to do with how they are created and who ends up responsible. National Parks must be created by Congress, National Monuments can be created by the President or Congress. (And I guess if there was ever a lawsuit about the status of such a site the Supreme Court would get in on the action? I wouldn't want them to be left out.) The Park Service runs the National Parks and some other agency ends up running the National Monuments.

Why do I bring this up? Because this past week I visited Newberry National Volcanic Monument, managed by the US Forest Service and I saw no difference between it and a National Park (except maybe for the fishing in the lakes). I feel like this is an indicator of the disfunction and cost of politics, that we need two mechanisms to essentially do the same thing because sometimes, one of them isn't working. I as an engineer I should appreciate the redundancy but it seems like redundancy in governance commonly goes by the name "waste". At least they all figured out having a common location to reserve campsites is a good idea.

OK, let's not dwell on such things any longer.

Last week we went camping! It was mostly wonderful aside from being very cold at night. Our one year old end up sleeping with us and with a cap on his head, he slept through the night like a champ. Being an outdoor boy, he loved having ready access to sticks, dirt and rocks.



My big event for the trip was a hike to a local peak. It was the most ambitious hike since injuring my foot with a 1400 foot increase in elevation and a total length of around seven miles round trip. I was hauling my son in a baby-carrier backback most of the way and I paid for a bit the next day but it was worth it. A view from along the way, showing the two lakes in the Newberry volcano caldera:



When we got to the top we found that there is a road that allows mere mortals to drive directly to the peak. The next morning, when our son woke early with the sun, being a mere mortal at that hour of the day, I drove up with him to get some sunrise pictures. The mountains you see are Mt. Bachelor and the Three Sisters.



The only other hiking we did was a short mile or so to the Pauline Falls; here's the view from the lower observation platform.


Sunday, July 10, 2016

Backyard Landscaping - Moving the Fill Dirt

Starting to move the pile of dirt in the drive to hole in the backyard.  Here's how the pile looked as left by the dump trucks. The dirt on the right is fill dirt and the that on the left is nutrient-rich topsoil.


I did a little work on Thursday but the majority of the action was yesterday with the help of my wonderful friend SamHam.


Here's how those piles looked after few hours of labor:


And the backyard:


I count roughly fifty wheelbarrow trips to get to this point but its a little tricky to tell because, as I was using the wheelbarrow, SamHam was carrying five gallon buckets of dirt and filling in some of the gaps I was leaving.  You know, just carrying big buckets of dirt like he did this everyday.

We made a good dent in the pile and I'm sure we'll have more to move. The next step will be smoothing out those piles and compacting them so I can get a good idea how much more of the lighter-colored fill dirt needs to be moved before we start moving in the darker-colored topsoil and plant the grass.

Thursday, July 07, 2016

Backyard Landscaping - Bermuda Grass Barrier Installed

Finished last night:


Bermuda grass, the type of grass that pretty much covers our back yard because it does so well in the hot and dry climate we have here, had begun to chip away at the edges of our basketball court before this renovation. The asphalt was thin enough and the gras strong enough that it was actually growing underneath and then breaking through and apart the asphalt.

To help prevent that, I've installed a five-inch plastic barrier around the edge of what we retained of the court. I don't know how effective it will be but it was inexpensive and didn't take much labor to put into place. What you see above is my use of bricks to hold it flush to the asphalt; I'm hoping the sun will help relax the plastic from its rolled-up state into something straighter and I'll be using some tar-ish roofing adhesive/sealant along the top edge to help to stick to the asphalt.

At the two corners I've installed two notched PVC pipes to protect both the asphalt and help provide a stronger connection point for the barrier. Again, the roofing adhesive will be applied liberally to help secure the entire thing.

Again, I don't have high expectations but I wanted to try something to keep the Bermuda at bay.

Monday, July 04, 2016

Backyard Landscaping - Temporary Sprinkler System Install Complete-ish

Two trips to the home improvement store later, the temporary sprinkler system is in place.


Four heads at the corner of the court. The front edge of the asphalt has a plastic barrier I'm going to try using to keep the grass from being able to grow under the court and break it up, as it was doing at the edges of the original court. I'm skeptical of how effective it will be but I figure its worth a little extra cost and effort.

Backyard Landscaping - Beginning Sprinkler System Install

Completed last night and photographed this morning:


The trench on the left side is to support two new sprinkler heads placed at the corners of the court. There will be a parallel trench and sprinklers on the other side as well. The water for both of these will come from an existing line with three heads running along the back fence. Eventually there won't be any grass back there thus my choice to repurpose the line.

Saturday, June 25, 2016

Backyard Landscaping - Gravel Removal Complete

I finished this up a few days ago after the sun had gone down and thus had to wait to get the picture.


Next up, hack in a few extra sprinkler heads to kind of get some water in the now removed court. This is going to be temporary as I'm planning to redo the sprinkler system as a whole but that won't be for a while.

Tuesday, June 21, 2016

Backyard Landscaping - Gravel Removal

Progress from last nights efforts. You can take pictures like this at 9pm in the evening when you live up north. Also, you can stay up later to work as well. I guess that's a bit of a two-edged sword.


Maybe another hour or two and I'll have it all out.

Saturday, June 18, 2016

Backyard Landscaping - Basketball Court Asphalt Removal

When we purchased this house, the backyard was mostly a basketball court. Wanting to have more than asphalt to enjoy in the summer, we have been planning on how to improve things. A few weeks ago, that process began.

First, here's a very mediocre picture of what we were starting with.



I was getting some help with the asphalt (and gravel) removal and wanted to estimate how long I thought the job would take so I marked off a small fraction of the court and timed how long it took me to get it done. Verdict, 24 man-hours of labor to removal the full 30' x 40' court.



The help came and over the course of two evenings we got all the asphalt up; they weren't interested in sticking around for the gravel so that's what I've been working on infrequently over the past week or two.


We decided to keep a key-sized piece of the asphalt along with the fancy, very sturdy, and expensive basketball hoop. As I've been removing the several inches gravel that was laid beneath the asphalt I've been piling it in the key for use later in the project. The area on the left that is mostly brown has had the gravel removed (more or less); the area on the right that is gray has yet to be addressed.  I'm guessing is will be another two hours to get it done.

Stay tuned for further pictures of the yard as the project slowly evolves.

Thursday, May 19, 2016

Canonical OK Go Videos

You know OK Go; they're the band that does the creative and adventurous music videos. I've linked to their videos in the past and I will do so again but this time in a much more rigorous manner. Which is to say I intend to provide a chronological and canonical list of their videos. Note that this list will not include all of the music videos attributed to them, just the real ones, if you know what I mean.

A Million Ways


Here it Goes Again


This Too Shall Pass (Marching Band)


This Too Shall Pass (Rube Goldberg)



End Love



White Knuckles



Last Leaf

All is Not Lost



Needing/Getting

The Writing's on the Wall


I Won't Let You Down


Upside Down & Inside Out









Saturday, April 16, 2016

Status Light

When moving into our new building at work, we were all given little status lights to place on the top of our cubicle walls; they looked like this:


You plug on end into your computer and it looks at your Lync status to determine if you are available, busy, or away from your computer. They are a handy way to determine at a glance if somebody is available for an impromptu meeting.

And they only work on Windows' computers. Having a Mac, I've been out of luck.

Recently fed up with the performance of Lync as my softphone, I requested a desk phone and the one I got (Polycom CX600) logs into Lync from the phone. Furthermore, it has a little circling arrow icon on the phone that lights up with the appropriate status color. 

And that's when I decided to make my own version of the status light.

In what is surely the least attractive way of doing this, I've mounted a color sensor over that double-arrow icon and use it to determine my current status. With that information, I drive a string of RGB LEDs to the appropriate color. My cube looks something like this and I've installed the LEDs so they shine on frosted glass above my desk.

Details:

  • Color sensor is TCS230. You toggle two control (S2 and S3) pins to select the color to measure and it varies the frequency of a square wave depending on the intensity of that light. You can reduce the overall frequency of the device (pins S0 and S1) to a few different levels and I choose the lowest scale to help make the measurement easier. Measuring frequency is done by...
  • ... Arduino's have a pulseIn function that can be used to measure the length of half a period of the output frequency. When a given color is very dark the output frequency is low and the pulse length is long. When the color being measured is bright, the frequency is high and the pulse length is short. Based on these measurements, I can classify the input color as red, green, or yellow and set the output PWM period of three transistor driving those colors on..
  • ... two meters of RGB LEDs. These are 12V LEDs and they are all one color, different than those that I used for my Lorenz project. I used some higher-power transistors I have laying around MPT16N25E to drive them.  Maximum current for these lights is 2A which is being split across three transistors. That current would only show up for maximum bright "white" light (full red, green, and blue power) so I don't really have much to worry about. 
  •  I used a a 250V 2A polyfuse for protection on the 12V supply. The power supply plugs into the Ardiuno barrel jack and then I tap the "Vin" pin for the 12V I need for the LEDs. The on-board polyfuse protects the Arduino but not the transistors and LED string. 
The system has been running for a week or two without much incident. The only tricky part was getting the sensor firmly attach to the light on the phone so the color measurements were consistent. I've been getting good feedback from my co-workers, especially those that realized it was more than decoration. Overall, I think we could call this a success.


Here's the schematic, picture of the board as completed, and source code:

   
 // Used to measure the status light color off a Lync-enabled desk phone and drive RGB LEDs to the same color  
 // 2016 Trevor Hardy  
   
 const int pinTCS230_S2 = 3;  
 const int pinTCS230_S3 = 4;  
 const int pinTCS230_Out = 2;  
 const int pinRedLed = 9;   
 const int pinGreenLed = 10;  
 const int pinBlueLed = 11;  
   
 const unsigned int max_width = 65000; //determined experimentally.  
 unsigned int PW_divisor = 0;  
 unsigned int pulseWidth = 0;  
   
 int TCS230_red = 0;  
 int TCS230_green = 0;  
 int TCS230_blue = 0;  
   
 int red_out = 0;  
 int blue_out = 0;  
 int green_out = 0;  
   
 // the setup routine runs once when you press reset:  
 void setup() {  
  Serial.begin(250000);  
  // declare pin 9 to be an output:  
  pinMode(pinRedLed, OUTPUT);  
  pinMode(pinGreenLed, OUTPUT);  
  pinMode(pinBlueLed, OUTPUT);  
  pinMode(pinTCS230_S2, OUTPUT);  
  pinMode(pinTCS230_S3, OUTPUT);  
  pinMode(pinTCS230_Out, INPUT);  
   
  PW_divisor = max_width/256;  
 }  
   
   
 // the loop routine runs over and over again forever:  
 void loop() {  
    
  read_TCS230();  
    
  Serial.print("Red: ");  
  Serial.println(TCS230_red);  
  Serial.print("Green: ");  
  Serial.println(TCS230_green);  
  Serial.print("Blue: ");  
  Serial.println(TCS230_blue);  
    
   
  if ( TCS230_red > 200){  
   //Red  
   red_out = 127;  
   green_out = 0;  
   blue_out = 0;  
   Serial.println("Red\n");  
  }  
  else if (TCS230_green > 120 && TCS230_red < 120 ){  
   //Green  
   red_out = 0;  
   green_out = 127;  
   blue_out = 0;  
   Serial.println("Green\n");  
  }  
  else if (TCS230_red > 150 && TCS230_green > 150){  
   //Amber  
   red_out =200;  
   green_out = 63;  
   blue_out = 0;  
   Serial.println("Amber\n");   
  }  
  else {  
   red_out = 0;  
   green_out = 0;  
   blue_out = 0;  
   Serial.println("Off\n");   
     
  }  
    
  analogWrite(pinRedLed, red_out);  
  analogWrite(pinGreenLed, green_out);  
  analogWrite(pinBlueLed, blue_out);  
  //delay(1000);  
 }  
   
 void read_TCS230()   
 {    
  digitalWrite(pinTCS230_S2, LOW);   
  digitalWrite(pinTCS230_S3, LOW);   
    
    
  //pulseWidth = pulseIn(pinTCS230_Out, digitalRead(pinTCS230_Out) == HIGH ? LOW : HIGH);   
  pulseWidth = pulseIn(pinTCS230_Out, LOW);  
  if (pulseWidth == 0){  
   pulseWidth = max_width;  
  }  
  //Serial.print("Red pulsewidth: ");  
  //Serial.println(pulseWidth);  
  TCS230_red = 255 - (pulseWidth/PW_divisor - 1);  
    
    
    
  digitalWrite(pinTCS230_S2, HIGH);   
  digitalWrite(pinTCS230_S3, HIGH);  
  pulseWidth = pulseIn(pinTCS230_Out, LOW);  
  if (pulseWidth == 0){  
   pulseWidth = max_width;  
  }  
  //Serial.print("Green pulsewidth: ");  
  //Serial.println(pulseWidth);  
  TCS230_green = 255 - (pulseWidth/PW_divisor - 1);  
   
    
  digitalWrite(pinTCS230_S2, LOW);   
  digitalWrite(pinTCS230_S3, HIGH);  
  pulseWidth = pulseIn(pinTCS230_Out, LOW);  
   if (pulseWidth == 0){  
   pulseWidth = max_width;  
  }  
  Serial.print("Blue pulsewidth: ");  
  Serial.println(pulseWidth);  
  TCS230_blue = 255 - (pulseWidth/PW_divisor - 1);  
    
 }  

Tuesday, March 15, 2016

Antenna Pre-amp - Part 3 - Switch Board and Antenna Jacks

Last time I left-off having built a second version of the pre-amp proper using the designers suggested layout and the circuit worked much better (as measured by the steady-state current).  Now onto the rest of the pre-amp box.

Again, taking cues from the original design, I decided to include attenuators, both before and after the amplifier. To do this, I used one of many calculators online to calculate the resistors for a T-style attenuator. The resistors were ordered as a part of the original pre-amp parts order. Metal film, 1%, still pennies a piece. I also decided to include an antenna switch in the design.

When it came to construction, wary of my previous inattentiveness to layout, I wanted to do the best I could to keep the leads short and provide a solid ground plane, even just for the attenuators. I decided to use a strip of PCB scrap as the common ground throughout the signal path. I also decided, in what is likely an abundance of caution, to use RG-316 coax between the attenuators.

The attenuators would be switched, and I used the leads of the switch as mounting points for the resistors. The switches were DPDT, bypassing the attenuator in one position and routing through it in another. Only the signal side of the attenuator was switched, the ground side was directly connected to the PCB strip.

Using an existing enclosure needing to finally have a project to house, I prepped the front plate for mounting the switches, punching guide holes for all the switches and drilling them out with a hand drill. The results were not the prettiest but most of the failings in my handiwork are hidden by the switches mounting hardware.

Here are the results as seen from the inside of the enclosure.





The back side of the enclosure was used to mount the three input antenna jacks, the output jack, and the power jack.  This was all much simpler; just drilling holes and mounting the hardware; the coax connections come later.



The final step comes next, completing the final assembly.

Saturday, January 16, 2016

Fixing the Vacuum Cleaner

Our vacuum cleaner stopped running for more than a second or two at a time so I tried fixing it.



After all of this work, I determined the motor was overheating for unknown reasons, even when no external load was being applied. Verdict: dead vacuum cleaner.

And I don't even get any spare parts out of this mess, just a pile of plastic to put in the trash.