Sunday, April 02, 2017

Backyard Textile Work

I like to think the do-it-yourself attitude I enjoy is rubbing off on my wife. She desired her bathrobe, a light pink be a different color and rather than paying $80 to send it off and have it dyed, she decided to do it on her own.

She purchased a bottle of dye from the craft store and the instructions made it very clear we were going to have to boil the robe in dye for half an hour. A bathrobe is not a small item of clothing and not having a pot large enough she decided we would need to do this outside over a fire in some kind of kettle. 

So she hunted down a kettle. Which is to say a metal garbage can.

We were all out of firewood for the season so she found somebody with some downed limbs that needed cleaning up and picked those up.

She had me build a fire-pit/kettle-stand out of some red bricks we've had laying around and grabbed the grill off our BBQ to help hold the trash can up over the flames. The BBQ lid became the lid for the trash can.

We partially filled the trash can with water to confirm no major leaks, set it up on the grill, and lit the fire.


Once the water was boiling (about half and hour later), we added the dye.


This caused come concern in our son.



After boiling the robe for half an hour, the work of the dye was done and we took the trash can off the fire to cool. Not wanting those hot coals to go to waste, we decided to have smores. This improved our son's outlook on the whole project.

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.