Sunday, December 10, 2017

Richland Home Temperature Measurement:

In what is likely to be a moment of nerd weakness, I have started on the Richland version of the home temperature measurement system I had up and running in Wichita. (This is ignoring many, many other things that I have on my to-do list such as:

  • Update/repair the digital picture frame (video driver is dead and I'm probably going to update to a Raspberry Pi 3)
  • Finish the retaining wall in the back yard
  • Debug the Lorenz project so I can take it into work with self-respect
  • ...)
Due to the layout of this house, I'm not able to very easily implement this as a wired system and have been hunting around for good wireless options. I decided on using Low Power Lab's Moteino which uses the RFM69 radio. The radio has range should be more than sufficient to cover the range of my house and the Moteino can come with the radio installed and associated libraries make it easy to get the system up and running.

As I found out today: in less than fifteen minutes I had the sample system up and running with one node sending packets and the other receiving them (and acknowledging back to the sender the reception). With another thirty minutes of work I had the remote programming also working. This will be important as I plan on putting some of these nodes in hard-to-reach places and having the ability to wirelessly update their code will be important.

This was the hard part made easy. Now the hard part for me: start thinking through how this system is going to work and get my head around wireless communication paradigms.

Thursday, November 30, 2017

Alternative Christmas Story

Katie: Do you remember from the Christmas story we read who Jesus' mother was?

2-year old Avery: Mary!

Katie: Very good! And who was Jesus' daddy? [Setting aside important theological distinctions.]

Avery: Joseph!

Katie: That's right! And where were Mary and Joseph going when Mary was pregnant with Jesus?


Monday, November 06, 2017

If you're going to run for office....

... and you want people to take you seriously, I recommend that you get a dedicated email address instead of using the one that implies you don't plan to show up to your new job because you're too busy golfing.

Monday, August 21, 2017

Eclipse in Brief

I don't have the time right now the write more (story of my blog this calendar year) but I thought I'd at least post this. Hopefully I be able to write the whole story in the next week or so.

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  
 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  
  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);   
  for (int idx = 0; idx < 2; idx++){  
   digitalWrite(13, HIGH);   
   digitalWrite(13, LOW);  
  set_frequency(s1Red, PWM_frequency);   
  set_frequency(s2Red, PWM_frequency);  
  set_frequency(s3Red, PWM_frequency);  
  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 ( {  
   Serial.print("Ok, Time = ");  
   Serial.print(", Date (D/M/Y) = ");  
  } else {  
   if (RTC.chipPresent()) {  
    Serial.println("The DS1307 is stopped. Please run the SetTime");  
    Serial.println("example to initialize the time and begin running.");  
   } else {  
    Serial.println("DS1307 read error! Please check the circuitry.");  
  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");  
  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]);  
  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]);  
  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]);  
   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);  
   digitalWrite(13, LOW);    
 void print2digits(int number) {  
  if (number >= 0 && number < 10) {  

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.