Monday, February 17, 2020

(#35) What's the Fun in That (Part Three) 

The Software   


Part one of the series "What's the Fun in That" constructed an elaborate electrical timer from a Raspberry Pi and an 8 Channel Relay board.

Here I'll quickly cover the software side.  There are a gazillion projects out there that will show you how to connect Relay Boards to the GPIO pins on a Raspberry Pi.  And, once connected, energzing the relays is simply a matter of driving the GPIO pins Low or High.

But here are a few code snippets that might help you along the way. We're doing this in Python. And we're using the RPi.GPIO library:


Import RPi.GPIO

    
import logging
import RPi.GPIO as GPIO
import time
           
class ChannelManager(object):
                             
    # ---------------------------------------------------------
    def __init__(self):
        self.socket_pin_assignments = {
            1: 2,
            2: 3,
            3: 4,
            4: 22,
            5: 10,
            6: 9,
            7: 27,
            8: 17}

   
 The "socket_pin_assignments" dictionary maps a socket number to it's connected GPIO pin number. The pin number depends on how we've wired up the relay to the Raspberry Pi.  If you choose different pins, or a smaller relay board then your values would be different.

Before going much farther, we set the library to use BCM style pin numbering and we tell the library we're going to be using the GPIO pins as outputs:


GPIO.setup()

    
try:
    GPIO.setmode(GPIO.BCM)
    for key, value in self.socket_pin_assignments.items():
        logging.info("Setting socket {} pin {} to 

                      GPIO Output".format(key, value))
        GPIO.setup(value, GPIO.OUT)
except Exception:
    logging.exception('Exception in setting up GPIO ports')

    


The Clapper!

When an "On" message comes in, this function is called:
    
def socket_on(self, socket_number, duration=900):
    if (socket_number <= 0 or socket_number > self.max_socket_num):
        logging.error('Invalid socket number {} passed to the on

                      command'.format(socket_number))
        return
            

    pin_number = self.socket_pin_assignments[socket_number]
    try:
        GPIO.output(pin_number, GPIO.LOW)
        self.socket_states[socket_number] = "on"
        self.socket_on_time[socket_number] = time.time()
        self.socket_on_max_duration[socket_number] = duration
    except Exception:
        logging.exception('Exception in setting socket {} 

                           pin {} to GPIO.LOW'
                          .format(socket_number, pin_number))
          


When an "Off" message arrives:

    
def socket_off(self, socket_number):
    if (socket_number <= 0 or socket_number > self.max_socket_num):
        logging.error('Invalid socket number {} passed to the off

                      command'.format(socket_number))
        return
            

    pin_number = self.socket_pin_assignments[socket_number]
    try:
        GPIO.output(pin_number, GPIO.HIGH)
        self.socket_states[socket_number] = "off"
        self.socket_on_time[socket_number] = 0.0
        self.socket_on_max_duration[socket_number] = 0
    except Exception:
        logging.exception('Exception in setting socket {} 

                           pin {} to GPIO.HIGH'
                          .format(socket_number, pin_number))
          

That's about all there is for 'the meat' of this software. In my code I also setup MQTT to send and receive messages (using paho-mqtt).

I use ZeroConf to locate the MQTT Broker dynamically (using zeroconf).




Meltdown Prevention

And I have a function that wakes up every five seconds to check if a socket has been left on for too long.  [ When sending an 'on' command, you also specify the maximum duration you want it left on. ]

            
def check_for_duration_exceeded(self):
  logging.debug('Checking for time exceeded on all on sockets')
  time_now = time.time()

  for socket_num, pin_number in 

            self.socket_pin_assignments.items():
  if self.socket_states[socket_num] == 'on':
     max_seconds_on = self.socket_on_max_duration[socket_num]
     total_seconds_on = time_now - self.socket_on_time[socket_num]
     if (total_seconds_on > max_seconds_on):
        logging.warning('Maximum On Time reached for 

                         socket {}. Sending off command'
                         .format(socket_num))
        self.socket_off(socket_num)

        

The MQTT Messaging

If nothing else, I'm committed to being Event Driven and Message Based. So at the core of the functionality are a handful of JSON formatted messages sent over MQTT.


Clap On!

To turn a socket on, send this message to the MQTT Broker:

   {
     "topic":"RELAY/RV8.1/CMD",
  
"datetime":"2020-02-13T13:19:16-07:00",
  
"id":"RV8.1",
  
"channel":3,
  
"command":"on",
  
"duration":3480
   }

Where Channel is the Socket Number [ 1..8 ], the command is "on" and the duration is the maximum number of seconds you want the socket left on.

Clap Off!

The off command is almost identical, with the duration parameter ignored.
   {
     "topic":"RELAY/RV8.1/CMD",
  
"datetime":"2020-02-13T13:21:21-07:00",
  
"id":"RV8.1",
  
"channel":7,
  
"command":"off",
  
"duration":0
   }

There are two more commands "allon" and "alloff" which affect all of the sockets with a single command.

The TPS Report

Lastly, the system publishes a status message, every minute with the current status of all sockets:

  {
     "topic":"RELAY/RV8.1/STATUS",
   "datetime":"2020-02-13T13:23:00-07:00",
  
"id":"RV8.1",
  
"states":[
         {
         "duration":0,
         "state":"off",
         "channel":1
         },
 
     {
         "duration":0,
         "state":"off",
         "channel":2
         },
     
{
         "duration":3480,
         "state":"on",
         "channel":3
         },
<etc...>
      {
         "duration":0,
         "state":"off",
         "channel":8
      }
    ]
  }





(#34) What's the Fun in That? (Part Two)  

Keeping the Water Pipes in the RV Warm  


With Over-complicated Electrical timer in hand, it is time to do some testing!

Our supplies include: a six foot section of 3/4" PEX Pipe from Home Depot, 1" Pipe Wrap from Jax Ranch and Home Store. Two inexpensive temperature sensors from Aliexpress. And water from the tap.

And of, course, the Roof Heat Cable from Lowes.

The heat cable is not wrapped around the PEX pipe, it's simply laid down lengthwise to the pipe and taped (about every 12") to the outside of the PEX.  

One temperature sensors is taped next to the heat cable.

The pipe is filled with water.
 


The PEX is wrapped with pipe insulation and the second temperature sensor placed about halfway down the inside of the pipe.


And the whole assembly placed outside on a winter's day - ambient temperature was mid 20's (°Fahrenheit).



Using the Raspberry Based Pi Electrical Relay system built in Part One, the heat cable is then energized and temperatures measured periodically.


 

Power was applied for 30 minutes and then stopped. It was 30 minutes on, 30 minutes off, then 30 minutes on and 30 off -- for three hours.

Testing shows that the cable starts warming immediately.  And within a minute or two the water is warming up inside the PEX.

Amperage measurements shows that the cable immediately consumed 300 watts and varied very little as long as the power was applied.

I repeated this test 7 times, there was little variation.


I also ran one test with the pipe empty -- no water -- to see just how hot things could possibly get.  Both temperature sensors reached 150°F quickly and stayed there for 90 minutes. Hot but still below the 180-200°F PEX limit.

Conclusion

My conclusions is: the 300 watt Heat Cable could be a solution for keeping the exposed PEX pipes in the motorhome from freezing.

Energizing the heat cable on for 30 minutes then off for 30, keeps the water inside the pipe well above freezing without getting the pipe too hot.

The upward trend of both heating plots show that an optimization would be to lengthen the time the cable is off.

Next up the software that makes this thing work!
 

Monday, February 10, 2020

(#33) What's the Fun in That?  

It's Cold in the RV!  

We've got a problem.  When we camp in the motorhome and temperatures drop well below freezing, the water lines freeze. Some astute readers are immediately offering solutions: stay home, camp in warmer climes, sell the motorhome.

But what's the fun in that? Those are easy, simple, cost-effective and highly reliable solutions. And no fun.

The manufacturer of our motorhome clearly does not want their product used when temperatures dip below freezing. The waterlines are exposed underneath and essential (nee expensive) parts are also exposed for maximum damage potential.

Foolishly, I've crawled underneath and wrapped 99% of the exposed pipes with pipe insulation. At 14° Fahrenheit, I found out that the one percent missed was significant. We woke up to no water flow.

My fellow northern-hemisphere-equally-foolish-motorhome-owning readers will proffer a reliable, cost-effective solution at this point.  Carry ten bucks of RV antifreeze and spend fifteen minutes doing a quick 'winterize' step before going to bed.  You're right. That would work and be cheap.

But what's the fun in that?

Nope - let's see if we can come up with a way to over-engineer a solution here.  Let's take a cue from the Heated Water (inlet) hoses you can buy and see if we can keep the lines from freezing by keeping them warm.



Heat Tape and Heat Cable

After spending perhaps eight hours, in aggregate, reading blogs posts and manufacturer product sheets, I've learned there are at least a dozen variations on 'heat tape'.  Heat tape is an electrically charged wire that you wrap around the exposed pipe and plug into an electrical source. The heat tape, um, heats. It gets hot. The heat keeps the pipe from freezing.

To cut to the chase, there are a handful of challenges with using heat tape. First is that no manufacturer recommends their product for the use I'm aiming at. Nope. The motorhome pipes are PEX, not copper. The tape gets too hot.  Second is the expense. Third is the power consumption.  Those are just the top three.

There's a similar product that, the best I can tell, is called Roof Heat Cable. It's similar in function but is
apparently aimed at homeowners whose roofs are susceptible to ice dams.  [ At this point, if you're a native Texan, Floridian -- e.g. someone who knows not of ice dams, feel free to Google. ]

I had some heat cable. About 60'. Unused since I added ice-dam prevention sheathing when we re-roofed the house.


300 Watts All of the Time

One cold December day, I grabbed the Heat Cable and my Kill-A-Watt meter and plugged it all in. The Heat Cable I bought consumes 5 watts per foot. My 60 foot cable consumed 300 watts. The Kill-A-Watt meter confirmed it.

Some more expensive products can be thermostatically controlled. Not mine. When plugged in, it's on all of the time.

I filled a bucket with water, coiled up the cable and immersed the coiled cable in the water. After about 10 minutes, the water was 140° F.  That's a bit warmer than I'd like. PEX can handle it, but I'd prefer something cooler under there.

Jumping ahead, my idea was to pulse the electricity to the heat cable.  To come up with a way to turn it on for -- oh say-- 10 minutes, then off for 20 minutes. Then back on, then back off.  Lather-rinse-repeat all night.

I could probably find an inexpensive reliable timer to do this. But, what's the fun in that?
No there has to be a way to make this much more complicated, more expensive and less reliable!

[ January 2024 Update: this winter camping trip, I did use an inexpensive timer. I set it for 30 minutes on, 60 minutes off. Worked great! ]




Another Day Another Pi

Yes, you guessed it. My solution is to take a Raspberry Pi, a relay board, some wire and some sockets and craft a complicated solution.

I chose an 8 relay board, when two would have been sufficient.  I won't cover the specifics of wiring the relay board to the Raspberry Pi as there are dozens of posts that do.


Raspberry Pi and 8 Channel Relay Board

I removed the common tab from each of the four receptacles so I could control all eight sockets independently.


Removing Tab That Ties Two Together
Next is time spent with wire cutters, strippers and a couple of screwdrivers wiring it all up.




The components were secured to a board and then mounted in an inexpensive plastic tool tote from Walmart.  Main line was connected, proper wiring tested.




At this point

I've successfully created an 8 port 120V Glorified Timer. The relays are rated at 10A but because of the wire gauge I used, I will limit the current to each socket to 5A, 500 watts. And the total current will not exceed 1000 watts.

Now, On to part two - real world testing!