Swimming Pool

Blogspot limits the number of pages I can have, so I'm putting all my pool changes on this page as I get to them.  I installed a Hayward variable speed permanent magnet pool pump to save power when filtering the pool.  This led to problems with the solar heater sucking air at low speeds so I installed a valve on the roof to allow it to drain for freeze protection.  Not much point to having a low speed pump if you have to run it at high speed to support the solar heater.  I also built my own interface to the Goldline controller I have that allows control and monitoring from anywhere.  That's right, if there is an internet, I can control my pool.



Controlling the Pool 
    I have decoded a significant portion of the Goldline control protocol.  This is important because now I can control the actions of my pool from anywhere I want.  Sure there are systems out there that already do this; if you want to spend several hundred dollars and pay extremely expensive pool repair people to come to your house to install it.  Then after a year, when the warranty runs out, you get to pay them again and again.  I fully intend to control my pool to my own tastes, not work under the limits of some automation controller and system.  Besides, the only device that directly interfaces a computer to the pool controller, the AQ-CO-SERIAL is overpriced, and although simpler, still doesn't tell you something as simple as, the light is on, is a reasonable fashion.  I want to type 'l' and have the light turn on, or create a web page of my own design and push a button labeled "Light".
     The first control software that I developed is below.  I place it in the public domain for anyone to use and improve upon asking only that you update me if you discover anything that will make it work better or expand on the protocol description.  Naturally, it involves an arduino controller with support devices and will expand and (hopefully) improve over time, as will yours.
     Please understand, I have decoded a lot of the protocol, but there are portions that still escape me.  So things like comm buss addresses and some responses are not yet clear.  I may eventually decode some of these, but for now, control and monitoring are my immediate concerns.
   
GoldLine Protocol:

The protocol is obviously something that evolved rather than something that was designed fully capable and carefully deployed.  To understand why I claim this let me discuss a couple of the remotes and how they are supported.  There is one remote that has an LCD display that is 16 characters wide and two lines  tall; the controller sends the actual text that is displayed every couple of seconds properly formatted for this display.  There is a different remote that has a display that is 20 characters wide.  There's a specially formatted packet for this controller as well.  As you can see, there is a lot of traffic on the RS485 buss.  But just to make it even worse, the controller sends a packet that seems to be a keep-alive signal at sub-second intervals.  This packet doesn't seem to actually do anything but it sure fills up logs when you are recording the traffic.


10 02 01 01 00 14 10 03

     This is the keep-alive packet.  The first two bytes indicate the beginning and the last two indicate the end; I have no idea what the intermediate 01 01 do, but the 00 14 that precede the ending indicator is a checksum.  10 + 02 + 01 + 01 = 14 (all numbers are hexadecimal); they count the first two bytes (beginning indicator) in the checksum, but not the ending two bytes.  So, if you watch the protocol fly by you will see the actual text that is shown on the various kinds of displays.

01 03 50 6F 6F 6C 20 54 65 6D 70 20 20 38 34 DF 46 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 00 07 57 

     Above is an example that flew by while I was watching.  I left off the beginning 10 02 and ending 10 03 and captured the payload data.  The payload starts with a 01 03 that means it is intended to be displayed on the LCD screen and the formatting is for a 16 character display.  The text to be displayed follows and it ends with the checksum. This packet tells me  "Pool Temp  84 F".  There is a similar packet for every thing displayed on the remotes except the software revision that appears when you first turn it on and the "Updating Display" message.  Needless to say, with the plethora of display types available, there are a lot of this kind of message.  One of the difficulties is that the protocol actually changes when a new device signs on to the RS485 half duplex comm system.  For example when I turn on my wireless remote it sends a packet like:


10 02 01 02 0E 00 00 00 20 00 00 00 00 23 10 03

From that point on, until the remote has been off for around 20 seconds, there are packets formatted for a display of 2 lines of 20 characters added.  The status LEDs are another display and they come in a special packet specific to the various displays that has bit fields telling which LEDs should be on.  These packets look like:


01 02 0E 00 00 00 20 00 00 00 00 43
01 02 2E 00 00 00 20 00 00 00 00 63

     These two packets are especially interesting.  The 2E above shows that the filter is on, that's the 2 part, the E part means something else.  Notice that in the second packet the 2 is gone leaving only the E?  The first packet turns the light on and the second turns the light off.  That means the filter light is flashing as each packet is decoded.  The first 2E has a corresponding 20 a little further down the packet, that 20 means the first 2E is supposed to flash.  So they are supporting displays smart enough to see the second bit and flash the light themselves as well as displays that can't and need something else to flash the light for them.  So, if you want to decode this packet you have to handle the filter bit going on and off with the packets.  These are the bits that I have decoded.  There are two sets of 4 bytes where the first 4 directly correspond to the second with the second indicating that the corresponding LED is supposed to flash.

Byte
Bit
LED
Byte
Bit
LED
1
0
Heater1
2
0
Aux2
1
1
Valve3
2
1

1
2
Check System
2
2
Aux3
1
3
Pool
2
3
Aux4
1
4
Spa
2
4

1
5
Filter
2
5

1
6
Lights
2
6
Valve4
1
7
Aux1
2
7


     There are other packets that don't fit the description above.  Some of them appear to come from the variable speed motor I have and change when the motor changes speeds on its own.  There's also one packet that seems to be telling me the line voltage to the motor.  I have also seen packets that don't terminate with the 10 03 and just end with the 03.  These are display packets and could be for a display I'm not familiar with.  There is also a combination packet that has the bit pattern for the LEDs combined with the display text.  That's a cool packet but it disappears when the corresponding remote times out and shuts off so I don't use it.  Remember how I described how the protocol changed above?  This is an example.
     Now, the good part, the control packets.  They follow the 10 02 .......checksum 10 03 pattern and have bit fields in the middle.  The bit fields are different from the above and correspond in some fashion to the buttons on the remote.  I couldn't figure out the pattern, so I only have data for the devices I use, if you need others, use the source code below to dump the packets as they are sent by your remote and add code as needed.  00, 83 are the command bytes sent by the wireless controller, below are the bit patterns from the various buttons.  These are repeated twice in the message so 00 83 00 01 00 00 00 01 00 00 would be a light button.  These are the bit patterns I have figured out
       80 00 00 00   filter button
       20 00 00 00   plus sign  (minus sign doesn't send sometimes)
       40 00 00 00   pool button
       01 00 00 00   right arrow
       02 00 00 00   menu button
       04 00 00 00   left arrow
       08 00 00 00   System off button
       00 01 00 00   Light  
       00 02 00 00   Waterfall    (Aux 1)
       00 04 00 00   Fountain     (Aux 2)
       00 00 01 00   Solar Heater (Valve 3)
       00 00 02 00   button on bottom
       00 00 04 00   button in middle

A complete packet to turn on or off the light is:
10 02 00 83 01 00 01 00 00 00 01 00 00 00 00 98 10 03

     Sorry, there is no light on command, only a light toggle.  So, if it's already on, the command above will turn it off.  Think of it as a pushbutton to change the light's state.  This can be problematic with multispeed motors since each command will step the motor one event forward.  That's where reading the LED commands comes in handy.  Now, let's talk about timing and speeds.  This is RS485, and is supposed to handle higher speeds over long distances and the data is running at 19.2KB so, with the huge traffic load, a lot happens really fast and can outrun code if you're not somewhat careful.  Especially if you're using a PIC that runs at lower CPU speeds.  Additionally, decoding the packet could take enough time that, when you send it, you collide with some other packet flying by out there. RS485 doesn't have collision protection and is half duplex, that means you don't have any idea if some other device is sending at the same time you are.  The method I use is to wait for one of the keep-alive packets and as soon as it ends I send a command.  That works OK, but there are failures on occasion, so I have to resend if the command collides with some other device.  The first hundred or so techniques I tried took too long to format or something and the failure rate was very high.  
     So, you have to format your command, wait for a keep-alive to come in and send the command as soon as you can after the keep-alive ends.  Using half duplex you keep the receive line low until the packet flies by and then pull the line high, send and then pull the line back low.  Actually, it's not as bad as it sounds, but it does take some care.  I suspect there would be significant problems using a normal computer such as a laptop because the OS would schedule something at the wrong time or a disk swap would delay the send.  There's also some significant delays using USB communication ports when you first start them up.  Hence, the Arduino.  Besides, the Arduino could be put right inside the controller case and an XBee link set up to remote the device to a multipurpose machine in the house somewhere.  Right now, I use a laptop USB connected to the Arduino with a long wire running out the back door to the pool controller; obviously not the permanent solution.

Hardware


As I said, this is RS485 so a special adapter is needed to go from the serial TTL of the arduino board.  I used this little device the Futurelec Mini RS422 adapter; don't let the name confuse you, it easily switches to half-duplex RS485, and at less than $5, the price was right.

And, of course, an Arduino.  I specifically got one a version back from the latest one just in case I needed to change the chip at some point.  These are also a bit cheaper and easy to find.

This is the device in operation hooked to the usb port of my laptop.  Using the laptop I can send the letter 'l' to the arduino, it waits until the right time and then send a light on command to the pool controller.  The plan is to interface something else to the arduino making it fully remote, then install it near the controller so there are no difficulties with wiring.  Notice that besides power and ground, there are only three signal lines to the RS485 converter.  Other than the obvious receive and transmit there is a line to switch between transmit and receive modes.  


     Over the next few days I'll adapt this to work with an XBee radio and mount it in a water proof box for installation near the pool controller.  The XBee will allow remote monitoring and control of the system.  I'll then interface it with my Home Controller Arduino and I can have as many timers as I want and use the other sensors around the house to generate commands to the pool.  For example, I can now tell the pool to turn on the lights any time I turn on the porch light, or keep the light off during the daylight hours so someone can't mistakenly leave it on.


Pool Controller Software

     Below is the Arduino script I came up with that actually can control the pool.  The command interpreter is extremely primitive, so you can modify it any way you want to meet your own preferences.  However, the techniques I used may need a little explanation.
     Since it can take a couple of seconds to find a place in the protocol exchange that will accept a command and then for the pool controller to actually react to the command, one can't send and expect an immediate response.  To overcome this problem I use timers set at two seconds to check to see if the command worked.  So, I send a command, wait 2 seconds check the result.  If the command didn't work, which likely means a collision, I resend the command and wait again.  The command usually works on the first try but every fifth time or so, a retry is necessary.  On the rare occasion there will even be a third try.  However, this seems to work well and is easily adapted to. The code relies heavily on various flags that are set while decoding the protocol and then acting on them as a separate action.  This helps the timing of the commands being sent.  That means that with a few changes you could implement such commands  as "lights off" by simply looking to see if the light was on before sending a command.  Maximum versatility is available this way.

The Arduino Sketch

/*

Goldline ProLogic control software.

This took a couple of months research and testing to get this far.  The pool controller
protocol is not documented anywhere, but there are a few subtle clues in various places.  I
put a LOT of comments in here to show what I'm doing and how I'm controlling the pool.

It's very difficult to decode the protocol and I haven't completed doing so.  However, I have
discovered enough to control my pool and you should be able to expand on my efforts to get
what you need done.

I want to be able to set up timers to turn the pump on whenever I want, not just the built
in 2 timers they allow me.  Additionally, it would be nice to be able to turn the pool light
on when the sun goes down and then turn it off maybe an hour later.  I want to be able to do
things the controller just won't let me do.

Also, I don't want to shell out several hundred dollars for a RS232 interface that is as hard to
use as the actual controller such as the AQ-CO-SERIAL_DS.  Granted, the person that developed
that device put in a lot of work, but they had access to Goldlines documentation as well.
Additionally, the menu directed interface of the AQ-CO-SERIAL_DS is silly.

I have implemented a very simple input scheme of type an 'l' and the light turns on.  A 'f' will
turn on the fountain, etc.  The filter is an 'm' since the 'f' was already used.  The command is
sent to the pool controller and then the status is checked to see if it happened.  If it doesn't,
the code will send it again.  This operation is controlled by a timer since some of the items can
take as much as two seconds to happen.  So, not only do we have to allow for RS485 collisions,
we also have to account for the time it takes to do something.  Talk about reality programming.

There's much more description of the protocol and timing considerations on my web site at
  draythomp.blogspot.com

I place this code and my descriptions in the public domain and encourage people to expand on it;
I ask only that any discoveries you make be passed back to me for eventual addition to this work
*/

#include <MemoryFree.h>
#include <SoftwareSerial.h>
#include <Time.h>
#include <TimeAlarms.h>

SoftwareSerial poolSerial = SoftwareSerial(2,3);
uint8_t buffer[200];
uint8_t* bufptr;
boolean sendPending = false;
#define dirPin 4
// bits for various functions
#define filterOn 0x20
#define lowSpeed 0x20  // same bit different byte
#define solarOn 0x02
#define lightOn 0x40
#define waterfallOn 0x80
#define fountainOn 0x01
// where I'll hold the data to give on request
// this was originally a series of boolean flags, that turned out
// to be harder to handle than a bit field.  If you need more bits
// at some point, use a uint16_t; that should hold you a while
uint8_t poolStatusFlag = 0;  // most recent pool status
uint8_t newStatusFlag = 0;
#define filterBit         0x80
#define filterLowSpeedBit 0x40
#define solarBit          0x20
#define lightBit          0x10
#define waterfallBit      0x08
#define fountainBit       0x04
int poolTemp = 0;
int airTemp = 0;
// Command strings to send to the pool controller
// Yes, I could construct these on the fly and been much more elegant
// for the computer purists out there.  However, this is much clearer
// and easy to understand for most folk.  Heck, you can just look at it
// and see how the protocol works.  It was a pain calculating the
// checksum though.
// So if you create a new one, keep the data from reading it and reuse
// the checksum value
uint8_t lightData[] =     {0x10, 0x02, 0x00, 0x83, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x98, 0x10, 0x03};
uint8_t fountainData[] =  {0x10, 0x02, 0x00, 0x83, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x10, 0x03};
uint8_t filterData[] =    {0x10, 0x02, 0x00, 0x83, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x96, 0x10, 0x03};
uint8_t waterfallData[] = {0x10, 0x02, 0x00, 0x83, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x10, 0x03};
uint8_t solarData[] =     {0x10, 0x02, 0x00, 0x83, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x98, 0x10, 0x03};

void showMem(){
  Serial.print("Free Memory = ");
  Serial.println(freeMemory());
}

void setup(){
  Serial.begin(57600);
  Serial.println("Initializing..");
  poolSerial.begin(19200);
  pinMode(dirPin, INPUT);      // this will set the internal pull up resistor on the
  digitalWrite(dirPin, HIGH);  //digital output pin
  pinMode(dirPin, OUTPUT);
  digitalWrite(dirPin, LOW);   // to receive from rs485
  memset(buffer, 0, sizeof(buffer));
  bufptr = buffer;
  setTime(0,0,0,1,1,11);  // just so alarms work well, I don't really need the time.
  // the timealarm library is a nice way to schedule something in a few seconds or minutes
  // this can be used to send commands also.  I do this later in the code
  // to report pool status after a command is sent
  Alarm.timerRepeat(60, poolReport); //Show status periodically
  Serial.println("done");

}
#define waitForFrameStart1 1
#define waitForFrameStart2 2
#define gatherData 3
#define waitForFrameEnd 4
boolean okToSend = false;

int frameStatus = waitForFrameStart1;
boolean checksumOk = false;
uint8_t* commandPtr = 0;

boolean firsttime = true;

void loop(){
  char c;

  if(firsttime == true){  // the arduino has limited resouces, check them
    showMem();            // this helps keep me under the limits
    firsttime = false;
  }
  if(Serial.available()){  // extremely primitive command decoder
    c = Serial.read();
    newStatusFlag = poolStatusFlag;
    switch(c){
      case 'l':
        commandPtr = lightData;
        newStatusFlag ^= lightBit;  // I can't believe it!  I actually got to use ^ for real.
        break;
      case 'm':  // the filter has three states off, high, low
        commandPtr = filterData;
        if (poolStatusFlag & filterLowSpeedBit) // if low speed, the filter is on, next =  off
          newStatusFlag = poolStatusFlag & ~(filterBit | filterLowSpeedBit);
        else if (poolStatusFlag & filterBit) // filter is on, but not low speed, next = low speed
          newStatusFlag = poolStatusFlag | (filterBit + filterLowSpeedBit);
        else
          newStatusFlag = poolStatusFlag | filterBit;
        break;
      case 'f':
        commandPtr = fountainData;
        newStatusFlag ^= fountainBit;
        break;
      case 'w':
        commandPtr = waterfallData;
        newStatusFlag ^= waterfallBit;
        break;
      case 's':
        Serial.println("Solar is thermostat controlled...silly");
        break;
      case 'R':
        poolReport();
        commandPtr = 0;
        break;
      default:
        commandPtr = 0;
        break;
    }
    Serial.flush();
    if (commandPtr != 0)
      poolSendFlag();
  }

  Alarm.delay(0);  //just to make the alarms work
  // the protocol has a 0x10, 0x02, data, 2 byte checksum, 0x10, 0x03 form
  // that makes it hard to parse since there are two characters to mess with
  // to add insult to injury, there is one packet type that ends with just a
  // 0x03 apparently to support an older or different display.
  // let's complicate it even more, there are packets in there that don't even
  // come close to any of the above.  They look something like
  // "00 E0 18 80 00 E6 00 18 9E 00 E0 1E 80"  and are probably related to motor
  // control.  Since Hayward took over the company and added support for their latest
  // motors.
  if (poolSerial.available()){
    c = (uint8_t)poolSerial.read();
    switch( frameStatus ){
      case waitForFrameStart1:
        if(c != 0x10){
//          printByteData(c);   //this will print data outside the 10->03 frames
//          Serial.print(" ");
          break;
        }
        else {  // this is a status or command frame
          frameStatus = waitForFrameStart2;
        }
        break;
      case waitForFrameStart2:
        if (c != 0x02){                   // it's not really the start of a frame
          frameStatus = waitForFrameStart1;
          break;
        }
        else {
          frameStatus = gatherData;
        }
        break;
      case gatherData:
        if (c != 0x10){
          *bufptr++ = (char)c;  // capture the characters inside a frame
          break;
        }
        else {  // nearly end of frame, check the checksum
          int len = (int)(bufptr - buffer);
          int i = 0;
          unsigned int checksum = 0x12; // sum of 0x10 + 0x02
          for( i = 0; i < len - 2; i++){
            checksum += (unsigned int)buffer[i];
          }
          if (checksum == (unsigned int)buffer[i] * 256 + (unsigned int)buffer[i+1]){
            checksumOk = true;
          }
          else {
            checksumOk = false;
          }
          frameStatus = waitForFrameEnd;  // wait for the last 0x03
          break;
        }
        case waitForFrameEnd:
          if(c != 0x03)
            break;
          else {
            if(sendPending && okToSend){  //this send has to come as soon after the end of packet
                                          // as I can possibly code it.
              poolSend();
              Serial.println("Command Send **************************");
              // It can take a couple of seconds for something like the motor
              // command to make all the way through the devices so...
              // makes you wonder why they have to run at 19.2K and create
              // so much traffic, doesn't it?
              Alarm.timerOnce(2, poolActionCheck); // in 2 second see if it happened
              sendPending = false;
              okToSend == false;
            }
            frameStatus = waitForFrameStart1;
            int len = (int)(bufptr - buffer);
//            printFrameData(buffer,len);
            if(checksumOk)
              processPoolFrame(buffer, len);
            memset(buffer, 0, sizeof(buffer));
            bufptr = buffer;
            break;
          }
    }
  }
}

void processPoolFrame(uint8_t* buffer, int len){

//  printFrameData(buffer,len);
  if(buffer[0] == 0x01){  // Display command
    if(buffer[1] == 0x01){  // some kind of keep-alive - ignoring for now
      okToSend= true;
      return;
    }
    // handle this like an interrupt.  Only set flags and deal with them after
    // the packet has been decoded.  There is not enough time to do serial prints
    // for information.  It's ok, to use them for debugging
    if(buffer[1] == 0x02){ // led display command
      poolStatusFlag = (buffer[2] & filterOn) ? poolStatusFlag | filterBit :
                         poolStatusFlag & ~filterBit;
      // controller trying to blink filter led for low speed indication
      poolStatusFlag = (buffer[6] & lowSpeed) ? poolStatusFlag | (filterLowSpeedBit + filterBit) :
                          poolStatusFlag & ~filterLowSpeedBit;
      poolStatusFlag = (buffer[2] & solarOn)? poolStatusFlag | solarBit :
                         poolStatusFlag & ~solarBit;
      poolStatusFlag = (buffer[2] & lightOn)? poolStatusFlag | lightBit :
                         poolStatusFlag & ~lightBit;
      poolStatusFlag = (buffer[2] & waterfallOn)? poolStatusFlag | waterfallBit :
                         poolStatusFlag & ~waterfallBit;
      poolStatusFlag = (buffer[3] & fountainOn)? poolStatusFlag | fountainBit :
                         poolStatusFlag & ~fountainBit;
//      printFrameData(buffer, len);  // use this carefully, will affect the command send
    }
    if(buffer[1] == 0x03){ // Display update command
//      for ( int i = 0; i < 32; i++){  // use this to show text intended for display
//        Serial.print(buffer[i+2]);
//      }
//      Serial.println();
      if (strncmp((char *)buffer+2,"Pool Temp",9) == 0){
        poolTemp = atoi((char *)buffer+12);
      }
      if (strncmp((char *)buffer+2,"Air Temp",8) == 0){
        airTemp = atoi((char *)buffer+11);
      }
    }
  }
  else if (buffer[0] == 0x00 && buffer[1] == 0x83){
    /*
       0x00, 0x83 is the command string sent by the wireless controller
       below are the bit patterns from the various buttons.  These are repeated
       twice in the message so 00 83 00 01 00 00 00 01 00 00 would be a light button
       80 00 00 00   filter button
       20 00 00 00   plus sign  (- doesn't send sometimes)
       40 00 00 00   pool button
       01 00 00 00   right arrow
       02 00 00 00   menu button
       04 00 00 00   left arrow
       08 00 00 00   System off button
       00 01 00 00   Light
       00 02 00 00   Waterfall    (Aux 1)
       00 04 00 00   Fountain     (Aux 2)
       00 00 01 00   Solar Heater (Valve 3)
       00 00 02 00   Unused switch on bottom
       00 00 04 00   Unused switch in middle
    */
//    printFrameData(buffer, len); // this will print command buffers outgoing from remote
  }
}

void poolSendFlag(){  // this flag routine tells the send code to go ahead
  sendPending = true;
}

void poolActionCheck(){

  if ((poolStatusFlag | solarBit) != (newStatusFlag | solarBit)){// solar is thermostat controlled
    sendPending = true;                                          // don't check it
    // This will now loop around through the frame handling and resend
    // which will set another timer to come back here and check again.
    // and so forth until the action has completed
  }
  else {
    poolReport();
  }
}
 


void poolSend(){
// This is rs485 and there is no collision protection.  It doesn't seem like the protocol
// goldline uses has an addressing scheme to enable the various possible control panels either
// Therefore....I just send it a couple of times and hope one of them gets through.
// So far, this seems to work just fine.  However, the commands have to be sent as SOON as
// possible after the end of a packet.  They must be only accepting commands for a timed period
// because I don't get on, off, on, off, types of reactions.  Ten commands only results in one
// action if they happen close enough together.
  for (int count = 0; count < 2; count++){
    digitalWrite(dirPin, HIGH); // to send rs485
    for(int i = 0; i < sizeof(lightData); i++){
     poolSerial.write(commandPtr[i]);
    }
    digitalWrite(dirPin, LOW); // to receive from rs485
  }
}
// this routine is where things are reported if you need it.  Tailor this routine
// any way you want to support your project
void poolReport(){
  Serial.print("Pool, ");
  Serial.print("Filter ");
  Serial.print(poolStatusFlag & filterBit ? "On, " : "Off, ");
  Serial.print(poolStatusFlag & filterLowSpeedBit ? "Low" : "High");
  Serial.print(" Speed, ");
  Serial.print("Waterfall ");
  Serial.print(poolStatusFlag & waterfallBit ? "On, " : "Off, ");
  Serial.print("Light ");
  Serial.print(poolStatusFlag & lightBit ? "On, " : "Off, ");
  Serial.print("Fountain ");
  Serial.print(poolStatusFlag & fountainBit ? "On, " : "Off, ");
  Serial.print("Solar ");
  Serial.print(poolStatusFlag & solarBit ? "On, " : "Off, ");
  Serial.print("Pool Temp ");
  Serial.print(poolTemp);
  Serial.print(", ");
  Serial.print("Air Temp ");
  Serial.print(airTemp);
  Serial.println();
}

void printFrameData(uint8_t* buffer, int len){

  // there's lots of nulls in there, can't just print it
  // so I print the hex values then the ascii below
  // so you can see the text as it flys by
  int i = 0;
  while(i < len){
    printByteData(buffer[i++]);
    Serial.print(" ");
  }
  Serial.println();
  i = 0;
  while(i < len){
    Serial.print(" ");
    if (isprint(buffer[i]))
      Serial.print(buffer[i],BYTE);
    else
      Serial.print(" ");
    Serial.print(" ");
    i++;
  }
  Serial.println();
}
// routine to take binary numbers and show them as two bytes hex
void printByteData(uint8_t Byte){
  Serial.print((uint8_t)Byte >> 4, HEX);
  Serial.print((uint8_t)Byte & 0x0f, HEX);
}


Actual Installation (with XBee)

Here's my version of the controller.  Notice that I put a hole in the box to plug in a laptop to change the programming and there is an XBee on the bottom with the antenna pointing down.  This version is totally remote controlled and works well.  I get power from the USB plug right now; it is hooked to a wall wart mounted inside my pool controller.  I will change that to a couple of wires leading into the box like the two RS485 wires that are on the bottom right.  Then I will plug all the holes in the bottom to keep the bugs out.  Rain shouldn't be a problem since the holes are all on the bottom.  Actually, I will leave one hole open for venting and drying with a small piece of wire mesh for the bugs.  I'm also going to paint it white to keep the sun from rotting away the plastic over time and keep it as cool as possible in the desert sun.




Other thoughts

It has occurred to me that I can do even better than this.  The Goldline controller only allows me two choices of speed at a time, low and high.  I can set the speed of each of these settings, but only get to choose between them.  The motor has provisions for four different speeds as well as timers.  Perhaps when I get this current project running I'll look into greater flexibility.  I may have to decode the link to the motor, but I could also just use the control input to the motor itself and disconnect it from the Goldline altogether.


Pool Solar Heater
I've had a solar pool heater for a few years now and love it.  For me, a pool is most comfortable running a only a little below skin temperature.  Yes, that means over 90F.  I know there are people out there that will argue this with me forever, but it's my pool, I'll keep the temperature where I want it.  So there.

However, when I put in the really cool variable speed pump, the solar heating system stopped working well because there wasn't enough pressure in the line to close the vacuum break on the roof.  So, the system sucked air and bubbled it out into the pool.  The air running through the system caused a drop in efficiency and the pool wouldn't heat as well.  I called around and no one had a solution to this except to turn the pump up so the higher pressure would close the vacuum break.  That just didn't make sense to me at all.  Why have a low speed pump that I have to run at high speed during the peak billing period to support a solar heater that is supposed to save energy?

I ran across a web site that discusses low head pressure on solar systems.  These folks present a really good case for lower pressure and have nice animated displays to illustrate their point (look here).  I called them and they were extremely nice, but they didn't have a solution either.  So, I thought about it a while and decided that I would stop the air from entering the system through the vacuum break.  I couldn't just close off the break, that would cause serious problem because the system wouldn't drain when not in use.  That could cause freezing in the winter and some other problems along the way.  What to do?  Put a valve in.

I got a valve like the ones that control the various features of the pool, ran some wires and installed the valve on the roof between the vacuum break and the heater array.  The valve is normally open and closes when the solar heater valve is turned on.  That way the air is cut off during use and allowed to pass when the solar is turned off.  It can still drain and the valve is (at least) as reliable as the rest of the system.  Cool Huh?


This is a picture of the solar array on the roof, the valve is on the right (not visible)

This is the valve and vacuum break.  The vacuum break is at the end of the white pipe on the left.  The system has been in place for a couple of months now and works really well.  No problems with it sucking air or leaking at all.

Update August 2011:

Spoke waaaay too soon.  The pressure of the water blew the connection to the valve apart.  Seems I need a barb fitting.  This turned into a major project that is detailed in blog posts on the main page.  Links:
Part 1Part 2Part 3Part 4, Part 5.  This was a real pain and took quite a while to figure out and fix.


The graph above shows the temperature over the last few days.  When the pump is off I can't monitor temperature since there is no water flow across the temperature sensor.  So, to keep the graph somewhat within reason I cut the temperature display off at 10F.  This graph is almost real time.  A quick note on some of the temperature readings:  If it goes over 100, it's probably because the water flow is so low that the temperature sensor is heating up from the direct sun; if it drops mid-day, it's because I'm doing something and shut the pool motor off and the water isn't flowing at all.  There has been one instance of my devices sending a huge number just once.  I can't get that to reproduce, but I'll look into preventing it.....sigh.


Acid Injection Pump
A salt water pool with automatic chlorination suffers from 'pH creep'.  This is where the chemical reaction of the chlorine generator causes the pH to rise over time.  If you don't pay attention to the pH constantly, you will get an alkaline pool that has a calcium ring around it.  It also creates 'snow'.  Snow is a fine white powder that circulates around the pool collecting in the corners and generally makes the pool look weird.  I got tired of messing with it every single day and decided to get an acid pump and automate it.

And this is where the story gets complicated.  Below is my first few tries at making this work.  I failed.  The pump I specify just wasn't up to the job and I finally had to admit defeat and start searching for something else to try.  I'll update this page when I have another idea ready to try out, but I'm going to keep the documentation on my failures as an example for other folk on what NOT TO DO.  Notice also, that this project has been ongoing since August, 2011; it takes a lot of time and patience to get something like this to work.

Update July 14, 2012: I have a new solution to this problem.  I am now using a peristaltic pump that has several advantages over the Hanna and seems to be doing well.  It is all detailed in my post here.  I'm still using the same timer configuration that is controlled by the House Controller and the same bucket and float to indicate how much acid I have left.  Let's hope this set of devices works out better.

The failure is described in excruciating details below:

I chose the Hanna BL 7-1 Dosing pump over a peristaltic pump because the peristaltic pumps tend to leak when they fail.  If you Google peristaltic pumps you'll see that they have a little tube that rollers run across to move the chemical.  This tube will dry out and break over time in a harsh, hot, outside environment (like where I call home) allowing the acid to drip inside the pump head.  I wasn't real fond of the idea of acid dripping in my pump head.  However, I have to put up with the noise a diaphragm pump makes.  This kind of pump works off a solenoid and the solenoid isn't the quietest thing in the neighborhood, but I only have to run it a few minutes a day.
However (there's always a freaking 'however'), there aren't any timers out there that can run a few minutes a day with the kind of control one needs for something like this.  It needs to be able to survive a power outage, know the current day in case you have to switch to every other day, know if the pool pump is running and control 110V directly.

So I built one.


This little device is an Ardweeny, an Adafruit XBee adapter board, an XBee and a relay.  I used this configuration because I wanted it to fit in a duplex outlet box like the pool controller above.  It's programmed to grab the time from my House Clock and activate the relay whenever I want.  Here's the device mounted on the fence hooked up to work.


I put the Hanna pump in a plastic box and mounted it beside the timer.  


The box was a problem though.  The dimensions of the box were given for the greatest amount in all directions and when I tried to put the pump in it, the lid wouldn't close because it was curved.  So, I broke out my heat gun, forced the lid shut and heated the lid until it set down properly.  Distorted the lid a bit, but it doesn't look bad and works quite well.


I inject the acid directly into the pool plumbing after the filter and before the chlorine generator with a little plumbing concoction that me and a guy at the plumbing supply came up with.


This actually works quite nicely.  It raises the pH while the pump is on and helps to clean the chlorine generator, plus the high volume of water at this point helps mix the acid well with the pool water.  It will take me a couple of weeks of checking and adjusting to get the right amount of acid per day to offset the pH creep, but that's better than forgetting and having my pool run amok.

Over time I plan to sample the acid level in a bucket below the dosing pump and turn on a light I'll add to my House Controller.  This way I won't have to check the bucket very often to make sure there's still acid in it.  The timer already sends data via XBee to the controller telling me it's alive and whether it's running or not.  Since it doesn't run very often, I have to stand there and watch for it though.  I may do some work in that area as well, it would be nice to know the last time it ran for example.

But the cool thing about this is that this is another little computer.  I can have it do any darn thing I want it to and add sensors or controls to it as I see fit.  For example, I'm thinking about adding a temperature sensor to the device so I have the temperature on that side of the house.  I could also put a moisture sensor in that area to check for leaks and such.  Cool.

Update 11/28/2011:  The fittings on the acid pump are starting to melt from the acid.  This is NOT supposed to happen.  I checked the specs carefully and talked to the pump company about what I planned, and the pump should be able to handle this just fine.  It's important to note that the pump housing, pump head, tubing and other stuff are not having problems.  Just the fittings for the valves.  There's some kind of material problem here.  I have detailed this here and here with pictures.  The problem is not resolved, I'll update as I go along.

Update 12/9/2011:  I talked to the manufacturer of the Acid pump and received a replacement pump head. When I disassembled the pump to install the head, I came across the root cause of my problems and detailed it here.  This problem is not resolved yet, and after it runs for a while, I'll try some of the ideas out.

Update 2/2/2012: Had to send the pump back for replacement.  Acid leaked into the controls through the shaft of the potentiometer on the front of the device.  More details here.

Update 3/5/2012:  Got the replacement acid pump back.  Now I'm testing the ideas I have for fittings and valves.  Details here.

Update 3/29/2012: OK, it's back in service and running.  All the valves have been replaced with parts that won't dissolve in the acid.  The system has been tested at much higher pressures and there are clamps everywhere.  The final configuration is here.  I may come back and change this part of the blog to reflect the reality of this project more clearly.  This has been a long time project; it seemed simple, get a pump and hook it up, but nothing ever seems to work the way they advertise.  Just for the record, here is a picture of the final configuration, compare it to the way it started out above.

The key thing to know is what plastics to use.  For example, the nuts I originally received with the pump were acetate, this will dissolve in acids.  The same thing happens with Nylon; the various fitting you find at Home Depot, Lowe's and such are often Nylon, beware.  Plastics like PVC, Kynar (PVDE), high density polyethylene (HDPE) will tolerate acids really well.  Metals are forbidden, not only will they dissolve or oxidize, they leave residue behind and will destroy other things.  Metal screws and such that will not be exposed to acid are OK until a leak happens.  So allow for a leak and give the parts plenty of ventilation to remove the corrosive fumes.  In my installation the pipe fittings are PVC, the tubing is HDPE, the valves are Kynar and the clamps are acetal.  This means the clamps are a possible weak link.  Since they are outside the tubing, easy to replace, cheap, easy to inspect, and serve a secondary function to backup the seal, I'm not too worried.  However, if I ever find Kynar, PVC, or HDPE clamps they will get replaced.  A special mention on the valves.  They are diaphragm types that do not have a spring.  There are mainly two types of check valves: ball and diaphragm; ball valves require a spring and it is usually metal.  Unless you were to use an exotic metal (expensive and rare) this won't work.  The diaphragm in the valve I used is Viton, another material that doesn't react to the acid.  For the picky people reading this, there are several other types of check valves; plunger and hinge come to mind, but they have disadvantages in this kind of installation; stick to either ball or diaphragm.

When you decide exactly what you are going to try first, be careful ordering.  The tubing I have is expensive and stiff.  It also is an unusual size.  This means fittings were hard to guess and I finally had to resort to using a digital caliper to actually measure things (gasp!).  So, a 3/16 inch barb fitting works well, while a 1/4 inch barb would work with some serious effort, and probably risk.  Since compression fittings work around the outside of the tubing, I used 5/16 inch fittings.  These (as described in my various posts) are a tiny bit too small and I had to resort to a heat gun to soften the tubing to make them work.

Here's a list of the parts I ended up with.  Note that I tried many combinations of parts and pieces getting to this state so read the various posts above that detail the various failures along the way.

US Plastic Item #: 57152

.307" x .342" Snap Grip Clamps, these are acetyl and could dissolve so order extras and inspect occasionally. 
US Plastic Item #: 61277

Kynar Connector Tube And Hose Fitting 5/16" x 1/4".  These are all plastic, no metal at all.  The thread size is only 1/4" so that means some adapters are necessary.

US Plastic Item #: 26001

3/8" x 1/4" PVC Threaded Reducing Bushing.  This is PVC and fits the top and bottom of the pump head.  One of the compression fitting above can be threaded directly into this and you can run tubing from that point on.  I also used this to inject the acid into the 2" PVC pipe that leads to the pool.  Notice though that this fitting has 8 sides so a socket won't fit it.  That's OK though since most of us use a pair of pliers to install this stuff.

US Plastic Item #: 64108

3/16" Kynar® Standard Check Valves.  This is the key item.  The body is Kynar and the diaphragm is Viton so it won't dissolve.  The break pressure is real low and it can withstand reverse pressure way above the swimming pool levels.  Too bad it doesn't have threaded fittings.

You'll also need elbows and nipples to direct things.  These are much easier to find and may even be available locally.  Remember they need to be 1/4" pipe thread.

Acid Pump Level Indicator
Sept 14, 2011:  I finished my first attempt at reporting when the pump reservoir bucket is getting empty.  Abysmal failure.  I detailed it in this blog post here .  Ever notice how, no matter how much you research something, there are things that sneak up to bite you?

I have another idea already in progress.  I'll report on success or failure when the glue dries.

Update Sept 15, 2011:  Failures like the one above don't deter me for long.  I have a working level indicator that will allow me to walk by and check it as well as have it reported electronically.  What I did was get some PVC tubing that would telescope inside a piece of 3/4 inch tubing.  This gave me something that I could run out of the bucket and put a switch on.  Here's the lid of the bucket and the float assembly:


It's a piece of 1/2 CPVC with a toilet float attached to the bottom of it.  On the lid I glued a flange and a piece of 3/4 PVC so I could mount a magnetic read switch outside the bucket.  The tube on the float has a magnet in it to close the switch when the acid level gets low.  The tube serves two purposes: the first I already mentioned, to hold the magnetic switch.  The second is to keep the float straight up and down in the bucket so it doesn't try to wander around.  Learned that lesson from the attempt with string.  Should have known better, but live and learn.  The piece of CPVC that is running across the lid is for stiffening up the structure.  These buckets are made as cheaply as possible and have a lot of flex.  This cross member glued on helped that problem a lot.


Here's more detail on the float and tube.  Notice I pinned the float on the tube instead of gluing it.  I didn't think I could find a glue that would work in the hot acid environment.  The marks on the tube represent the level of the acid in the bucket.  So I can walk by it and take a look to see how full it is.  Here's the magnetic switch:


I simply tie wrapped it to the 3/4 inch tubing.  This isn't the perfect solution since tie wraps dry out and fall off in about 6 months in my weather, however it will work while I get some experience with the level indicator.  I'll use a couple of stainless hose clamps to fasten it on better when I'm comfortable with the way it's working.  The magnetic switch is water proof, as most of these things are, but it may rot in the weather over time.  I'll see about wrapping it with some metallic tape to protect it in a week or two.  Here is the device installed and working:


One nice thing is that this lid is white already.  I'm going to paint the entire thing with a plastic paint soon, but being white will make that a little easier.  Ace hardware carries the white buckets if you need one.  The top mark on the rod is the empty mark, the next one down shows one gallon and the next two, etc.  So right now I have almost two gallons of acid in the bucket.  Nice to be able to walk by it and just look to see how it's doing.



30 comments:

  1. As I said before, I love what you're doing with the house/power monitoring.

    That Goldline protocol looks like a dog's breakfast.

    ______
    Rob

    ReplyDelete
  2. Actually, the protocol looks like what comes after a dog's breakfast. I couldn't believe they change it based on what devices are attached and active. Plus, the idea of sending a message to turn a light on, then a message to turn it off??? Where did they come up with that? My latest swimming pool project is an acid injector. This project is taking forever though. Fittings problems, plumbers that don't have a clue what they are actually doing once they get out of their basic routine, etc....sigh.

    ReplyDelete
  3. I am trying to decode this protocol as well, I have not been successful with sending a command. I have the AQL-P4 with a wireless remote and I suspect I am having an issue with the half duplex. Would it be possible for you to publish your header files so I can give your code a try on my setup?

    Thanks.

    ReplyDelete
  4. The code above is all there is of it. Notice that the only header files are the normal system ones for the Arduino. I use the strings I constructed to do each of the commands. There's a finite number or commands I use, so this method if pretty effective for me.

    ReplyDelete
  5. I figured out what my problem was, Thanks for your help.

    FYI-

    Here is the strings that work for my controller, they were a little different then what you are using.


    Filter
    10 2 0 5 2 0 2 0 7F 0 9A 10 3 On
    10 2 0 5 0 2 0 2 7F 0 9A 10 3 Off

    Lights
    10 2 0 5 20 0 20 0 7F 0 D6 10 3 On
    10 2 0 5 0 20 0 20 7F 0 D6 10 3 Off

    Cleaner
    10 2 0 5 10 0 0 10 0 0 7F 0 B6 10 3 On
    10 2 0 5 0 10 0 0 10 0 7F 0 B6 10 3 off

    Waterfall
    10 2 0 5 8 0 8 0 7F 0 A6 10 3 On
    10 2 0 5 0 8 0 8 7F 0 A6 10 3 Off

    ReplyDelete
  6. Wow, you actually have on and off commands. That will make controlling the pool a lot easier.

    ReplyDelete
  7. Those came from the AQL2-SS-RF Wireless remote. My keypad in AQL-P-4 generates a single command for On/Off similar to what yours is doing. I am using the RF remote commands for the advantage of sending an on of off.

    ReplyDelete
  8. I am trying to do the same, but I am using an mbed (mbed.org). There are a few details I may have missed.

    The AQL-P4 has 4 pins for wired remotes, you have two wires, which of the 2 wires are hooked to the AQL's which pins?

    Also, I cannot tell from the picture, but it looks like you have T+ and T- connected, right? Not T+ R+, right?

    ReplyDelete
  9. I can't remember which two wires I hooked into on the P4, I'll have to open it up and look. I'll try to get to it tomorrow and I'll post here. And, yes, I'm using t+ and t- on the rs485 adapter. The protocol is half duplex and the board I chose was a full duplex capable board. So, when that happens, you just use half of it for the protocol. I know, it's wasteful, but what the heck, these things are cheap.

    ReplyDelete
  10. Dave, thanks. I have the same rs485 adapter, I want to try and do the same thing you are doing, using the mbed instead (instead of adruino) and write an iPhone/iPad app to control the P4 from there. I have an ethernet cable I hooked up to the P4, I hooked up 4 wires (to the 4 terminals) and I just need to figure out which of the 4 need to be hooked up. I tried 1,2 and 3,4 to T+/T- and neither worked (I got nothing through the rs485 adapter), but I may have done something wrong.

    Also, I don't quite understand these lines of your code:

      pinMode(dirPin, INPUT);      // this will set the internal pull up resistor on the
      digitalWrite(dirPin, HIGH);  //digital output pin
      pinMode(dirPin, OUTPUT);
      digitalWrite(dirPin, LOW);   // to receive from rs485

    You set the pin to input, then set it to high.....then you set the pin to output? Why bother with the first step? Why do you want the internal pull up resistor on this pin? "High" is for send, right? SO when things are disconnected, you want it in send mode?

    Sorry for being dumb here...I am a programmer by profession, but haven't done anything embedded since college, so I am a little rusty on these intricacies..

    Thanks!

    ReplyDelete
  11. Here is my pinout.

    AQL P-4
    Red 1 is not Connected
    Blk 2 connects to T+/B on RS485
    YEL 3 connects to T-/A on RS485
    GRN 4 connects to Ground on RS485

    ReplyDelete
  12. Code Sample that I am using on linux with a USB - RS485 adaptor, it shows up as /dev/ttyUSB0 for me. I am sure you could make many improvements to this code.

    so once I compile that I can run it as ./a.out /dev/ttyUSB0


    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include


    int main(int argc,char** argv)
    {
    struct termios tio;
    struct termios stdio;
    int tty_fd,i;
    fd_set rdset;

    unsigned char input[128] = {0};
    unsigned char c='D';
    unsigned char l,pos;
    //10 2 0 5 0 20 0 20 7F 0 D6 10 3 Lights Off
    unsigned char lightsOff[] = {0x10,0x02,0x00,0x05,0x00,0x20,0x00,0x20,0x7F,0x00,0xD6,0x10,0x03};
    //10 2 0 5 20 0 20 0 7F 0 D6 10 3 Lights On
    unsigned char lightsOn[] = {0x10,0x02,0x00,0x05,0x20,0x00,0x20,0x00,0x7F,0x00,0xD6,0x10,0x03};

    // 10 2 0 5 2 0 2 0 7F 0 9A 10 3 Filter On
    unsigned char filterOn[]= {0x10,0x02,0x00,0x05,0x02,0x00,0x02,0x00,0x7F,0x00,0x9A,0x10,0x03};
    // 10 2 0 5 0 2 0 2 7F 0 9A 10 3 Filter Off
    unsigned char filterOff[]={0x10,0x02,0x00,0x05,0x00,0x02,0x00,0x02,0x7F,0x00,0x9A,0x10,0x03};

    // 10 2 0 5 10 0 0 10 0 0 7F 0 B6 10 3 Sweeper On
    unsigned char sweeperOn[]={0x10,0x02,0x00,0x05,0x10,0x00,0x00,0x10,0x00,0x00,0x7F,0x00,0xB6,0x10,0x03};
    // 10 2 0 5 0 10 0 0 10 0 7F 0 B6 10 3 Sweeper off
    unsigned char sweeperOff[]={0x10,0x02,0x00,0x05,0x00,0x10,0x00,0x00,0x10,0x00,0x7F,0x00,0xB6,0x10,0x03};

    // 10 2 0 5 8 0 8 0 7F 0 A6 10 3 Waterfall On
    unsigned char waterfallOn[]={0x10,0x02,0x00,0x05,0x08,0x00,0x08,0x00,0x7F,0x00,0xA6,0x10,0x03};
    // 10 2 0 5 0 8 0 8 7F 0 A6 10 3 Waterfall Off
    unsigned char waterfallOff[]={0x10,0x02,0x00,0x05,0x00,0x08,0x00,0x08,0x7F,0x00,0xA6,0x10,0x03};




    FILE *fp,*pooltemp,*poolsalt;
    fp=fopen("/tmp/pool-serial.out", "a+");
    //setlogmask (LOG_UPTO (LOG_NOTICE));
    openlog ("pool-lighs-on", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);

    //printf("Please start with %s /dev/ttyS1 (for example)\n",argv[0]);
    memset(&stdio,0,sizeof(stdio));
    stdio.c_iflag=0;
    stdio.c_oflag=0;
    stdio.c_cflag=0;
    stdio.c_lflag=0;
    stdio.c_cc[VMIN]=1;
    stdio.c_cc[VTIME]=0;
    tcsetattr(STDOUT_FILENO,TCSANOW,&stdio);
    tcsetattr(STDOUT_FILENO,TCSAFLUSH,&stdio);
    fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // make the reads non-blocking




    memset(&tio,0,sizeof(tio));
    tio.c_iflag=0;
    tio.c_oflag=0;
    tio.c_cflag=CS8|CREAD|CLOCAL; // 8n1, see termios.h for more information
    tio.c_lflag=0;
    tio.c_cc[VMIN]=1;
    tio.c_cc[VTIME]=5;

    tty_fd=open(argv[1], O_RDWR | O_NONBLOCK);
    cfsetospeed(&tio,B19200); // 115200 baud
    cfsetispeed(&tio,B19200); // 115200 baud

    tcsetattr(tty_fd,TCSANOW,&tio);
    i=0;
    pos=0;
    while (c!='q')
    {
    if (read(tty_fd,&c,1)>0){ // if new data is available on the serial port, print it out
    //fprintf(stdout," %x", c, pos); // print out data as it arrived
    if ( l == 16 && c == 3) { // end of command ready for sending
    // insert your own logic here
    write(tty_fd,&filterOff,sizeof(filterOff));
    c='q';
    }
    }
    l=c;
    }
    fclose(fp);
    close(tty_fd);
    closelog ();
    }

    ReplyDelete
  13. Awesome, thanks! I tried pin 1&2, and pin 3&4, but not 2&3...Interesting that you are grounding p4.

    ReplyDelete
  14. Here are my stats for my Pool http://mccoy-racing.com/wq/ I am using CO2 injection based on my PH. I am using a PH probe and ORP probe from Phidgets with their boar that converts to voltage. I am using a DS2450 on my One Wire bus using OWFS to convert the Voltage from the Phidgets board to PH. Seems to be working quite well with the controls sent to the AQL-P4. I am also using a Hitachi X200 VFD to drive a 3 phase motor connected to my old WisperFlow WFE-3. I ordered a 355201S from www.thepoolguystore.com for $151.32 a bargain compared to the VFD pump from Pentair. I have not setup the ModBus stuf for the Hitachi X200 yet but I am driving the dry contacts from the Parallel port and its working great all day long @ about 20 HZ.

    ReplyDelete
  15. I also do not ground it. I have the same pins otherwise. Middle two connected the two outside pins left open.

    You can tell why I used an Arduino, the board and its 485 converter both fit in a duplex outlet box. Makes it easier to place on the wall. These little arduinos boot in a couple of seconds so a watchdog timer to reboot when it has trouble doesn't present a problem.

    I'm starting work on an acid injector. My thinking is that acid is easy to get and an injection pump is simple to maintain over time. I expect to put a ph probe in the mix in a couple of months, but I've got about three other projects in front of it.

    pinMode(dirPin, INPUT);
    digitalWrite(dirPin, HIGH);

    The lines above are solely to enable the internal pull-up resistor on the digital pin. You set it to input, then set it high and it enables a pull up resistor.

    pinMode(dirPin, OUTPUT);
    digitalWrite(dirPin, LOW);

    This is the control for the pin itself. The dirPin (Direction Pin) switches between rx and tx on the half-duplex 485 board. In this case I'm setting it to receive.

    I took a different tactic on the pool. Instead of controlling it directly from the internet, I control another box that in turn, controls the pool. The other box also has controls for the A/C, and other stuff. I use that box to update Pachube and it has a display to tell me what's going on when I'm in the house. This way I can put timers on the second box to turn on the pool lights and such without worrying about how much memory I've used up on the little arduino.

    ReplyDelete
  16. I am having no luck. Two strange issues (note, I have no problem communicating to other device using my micro controller and serial)...

    1) I am getting nothing. I have things configured like you do (19200 baud, etc). I have wired the direction pin to ground, for now, I am only receiving, not sending. I have the RS485 board you have configured the same way you do in your above picture (the picture that also shows the Adruino).

    Next step, I will try going to my PC serial directly to see if I see anything, I suspect I will not

    2) When I have pin 2 and pin 3 connected to the RS485 board, and that board connected to my micro controller, which is then connected to my computer, via USB, I am getting a slight shock when touching the metal on my computer. Very strange...(not, the USB is powering the microconroller, thus the RS485 board).

    ReplyDelete
  17. That's weird. You're using the plug on the upper left of the P4 correct? That plug has an AC voltage on it of something or other to power some of the add on devices, but you shouldn't be getting anything from the two middle pins like you are experiencing.

    The way I tested the setup was to run a wire pair about 60 feet from the pool controller through my back door to the Arduino on my kitchen table. I was set up this way for a couple of weeks trying things. I had completely different luck, I only had to reverse the wires in the very beginning and I started getting data. Once I tried a few different baud rates I got data I could read and went from there.

    I'm actually set up exactly like the picture. I power the 485 board from the arduino and power the arduino from a 5V wall wart I picked up on eBay. I do not use pins 0 & 1 on the arduino, those are hooked to an XBee I have in the box for RF control. When I was testing it I used the usb connection to see the data.

    I'm at a loss on what could be causing your problem with the little shock.

    ReplyDelete
  18. I'm trying to work out the protocol of my Pentair Inteliflow VS pump while connected directly to an RS-485 shield of an Arduino, but I don't see any messages at all over the serial port. I suspect that my pump needs to be actively polled with a valid request before it will send anything.

    Does any of your other pool equipment have RS-485 ports, or is your Goldline controller the only device sending RS-485 communications?

    ReplyDelete
  19. I have the controller, motor and remote control sending commands. I'm using the Hayward motor and it has to be set to "Remote" for it to respond to the 485 port. I haven't tried to decode its protocol yet because the Goldline is doing it for me. I don't have a clue what the trick is to getting the Pentair to talk over the serial, but it may be the same kind of thing.

    ReplyDelete
  20. Ok, after further experimentation (my free time is limited), I am getting @12 Volts out of the RS232 TxRx pins, so my RS485 line driver must be misconfigured.

    I will have to look at the documentation to see what the heck I am doing wrong.

    ReplyDelete
  21. Where the heck are you getting 12V from? When I was hooked to my laptop I most I had running around was a little under 5V from the USB port.

    ReplyDelete
  22. I am supplying 5 volts. The pins from the Goldline were not 12 volts (obviously they fluctuated, but one pin was like 3v, the other was -3v or something). The Mini RS422 board (same one you use, same configuration) was outputting 12V through RX/TX (what is meant to connect to my MCU). Something is messed up here, I have ordered another RS232 to RS486 converter (I assume the one I ordered isn't RS232 TTL, but I have board for that separately).

    ReplyDelete
  23. Nice write up, thanks. Did my residency in Phoenix and can relate to viewing the swimming pool as an essential home appliance. Dont know if this "Industrial Liquid Level Sensor" might help but its surplus and only $8:
    http://www.goldmine-elec-products.com/prodinfo.asp?number=G17844
    Sounds like it toggles off when full so it may be bass ackwards for your needs. Enjoy the winter!

    ReplyDelete
  24. That thing is cool. Thank you. I don't think it will work for the acid injector, but there's this water softener that I keep forgetting about out in the garage. For eight buck and a light I can see when I drive into the garage.....

    ReplyDelete
  25. Excellent work!

    Regarding the communications protocol, I believe these are the specifications that would have simplified your efforts: http://www.hayward-pool.com/pdf/manuals/Manual343.pdf

    I agree that it is a poor protocol, e.g. button presses and display scraping!

    Cheers,
    John

    ReplyDelete
  26. Thanks for the link. When I want to add something, I'll dig into it. For now, I got so fed up with the silly way it worked I decided any changes can wait a year or two.

    It's probably the most annoying protocol I've ever worked with.

    ReplyDelete
  27. i was under the impression that the prot in the hayward link was the hex that came out via 232 "after" it goes through the $200 hayward adapter... i thought that it had some microprocessor that simplified it...

    ReplyDelete
  28. I'm not really sure what the heck the Hayward adapter actually does. The protocol from the controller itself matches pretty closely the protocol from the adapter. So, that doesn't leave it much to do except maybe level conversions, timing correction or something.

    I'm not willing to pay their price to find out either.

    ReplyDelete
  29. Really enjoy your site. I am working on a much smaller project controlling my pool. Wondering if you could give some advise.
    Question: Do you have arduino turn off and on your pool pump. If so what kind of realy do you use?
    Kenny

    ReplyDelete
  30. I control a Goldline pool controller with a device I made that tells it what to do, so I don't directly control the pool motor. However, I have looked at doing this a lot and plan to in the future because I just don't like having something in the middle that doesn't let me do what I want to do.

    Regarding your question about which relay, it would depend. For example, if you have one of those new variable speed motors, you can use a small relay that carries as little as an amp. If you are controlling one of the single speed motors, you need a bigger one. There are a thousand (or more) possibilities, so what you do is check the current draw of the motor and the operating voltage. You'll need a relay that has contacts rated for that. Then, you need to decide what will power the relay itself. If you're going to use an arduino, it can only supply 30ma at 5V, so you will have to have something that can go between the arduino and the new relay. If you're going to do something different, drop a hint here and we can discuss it a bit to get you started.

    ReplyDelete