Controlling Rs485 slave devices (heater, pump, SWG) directly with Pi

rekliner

New member
Nov 6, 2020
4
Nashville, TN
I'm not interested in shelling out for a pool controller and this is a fun project for me. Also, I have Hayward products mixed with Jandy so I think DIY would be my only option anyways. I find it amazing that all these devices have rs485 communications for their features and yet the codes to control them seem to be closely guarded trade secrets (judging from the takedown request for AqualinkD by Jandy).

ajohnson30 and Mutley have posted amazing programs that developed in these threads:
https://www.troublefreepool.com/threads/control-your-jandy-equipment-from-your-pc-with-a-15-adapter.27391
and Aqualink PDA automation using RS485


Unfortunately, all of these are efforts to communicate with a pool controller that in turn controls the slave devices. I see that Mutley started the AquapureD project to communicate directly with a SWG which is a great start... but without a controller to send commands to the pump or heater I don't have a way of reverse engineering their commands the same way. I know this is essentially what the Autelis products are doing, but their website says they are shut down for covid and they are not known for sharing that sort of info.

I'm at the point where i've identified the heater's device id as 104 (0x68) and it responds to a probe with an ACK. I'm trying to brute force out command numbers to see what flies but i've had no luck getting a response. Has anyone here monitored the commands and responses between the controller and slave devices? Mutley, did you ever work with a heater or pump?

The controller sends <header><device id><command><arguments><footer>. Header, device id, and footer are easy enough... and the set commands SHOULD be very simple. For example with the SWG setting the % is command 17 (or 0x11) and an argument value between 1 and 100. In theory, the heater temperature should be the same, just a different command number and presumably a temperature in farenheit. I imagine with the pump there is a command that selects the different speed presets. I figure i can deduce the responses to these commands in time. From there it's easy to integrate all of this into web controls and IoT data. Then Jandy can hate me too.

Has anyone else gone down this path?
 
Welcome to the rabbit hole.

There are some great threads around here for DIY pool controllers. You can check my build in my signature. My ideas are basically stolen from @cmc0619 who has a great build. @segalion has done some cool things too. @MyAZPool is officially losing his mind on the upgrades he is doing to his intelliCenter.

Here is the build that I started with.


For RS-485 control the best code base is based on NodeJS pool controller.


Ask questions and post your ideas and your build. We are happy to help and hopefully learn from you as well.
 
  • Like
Reactions: MyAZPool
I've had a start/pause/start again approach to this. Recently used the app to get me over the hump while waiting over a year for Pentair to fix trivial schedule change code they broke with and up date. This worked great an @rmontgomery did us all a huge favor by creating this app and helping a few like me get it up and running. Along the way I got to see all that can be done with Rpi. So I started playing with this and realized how a cheap fantastic little computer the size of a pack of playing cards could be so easy to set up. My big obstacle is programming and learning new languages since I haven't done programming for 40 years. Fortunately, there's a lot of software out there written by people that I can implement by just learning how to set it up.

So answer to your question is definitely yes. But it's not like we're all as capable as you. Some of us need more of a crutch in one area or another. Thankfully there are many like @Katodude , @guinness , @rmontgomery, @MyAZPool and several others that are making it possible for people like me to participate. I'm very steep on the learning curve and I think I"m getting close to have the capability to completely parallel my fancy, and very expensive Intellicenter.

Chris
 
  • Like
Reactions: MyAZPool
I'm at the point where i've identified the heater's device id as 104 (0x68) and it responds to a probe with an ACK. I'm trying to brute force out command numbers to see what flies but i've had no luck getting a response. Has anyone here monitored the commands and responses between the controller and slave devices?

Has anyone else gone down this path?

Well, this part anyway will sound remarkably familiar to a few of us here. We have been blasting Pentair IntelliValves with commands (and listening) for months. Nothing definitive as yet. Unfortunately there might be some IntelliValves destined for an early grave but maybe not. :( Still holding out hope....

You have some great resources here. I would not be anywhere near where I'm at with my new automation project if it weren't for the some of the good folks mentioned above...

Welcome and Good luck on your DIY endeavor.
Good times to be sure.
r.
 
the nodejs-PollController is an awesome resource and codebase, but since I don't have any intellitouch, intellivalves or pentair equipment the rs485 codes were completely different (I thought a thread here said that manufacturers had for a brief time come up with a common rs485 language but I see no sign of it). Also, since i'm skipping the middle man and hitting the devices directly it wasn't quite what I needed... I couldn't get it to run in standalone mode, but I didn't try very hard..my thought was I could put in some pull requests to make it understand my heater, swg, and pump - and i havent given up on that line of thought because I don't want to re-invent the web interface. AqualinkD is a fantastic suite but it's too tied to a physical control center for me to consider making a headless version.

So, since nobody appears to have posted any code for a jandy heater anywhere on the internet I went ahead and probed the heater unit. I haven't written an control program yet but I'll share what i found so far in case anyone else gets this particular wild hair:

The Jandy LXi/JXi heater needs to be pinged (68 00 00) or sent a command at least every 20 seconds or it shuts off all settings and reverts back to its built in control panel. This means if you had a heat set point on the control panel before you initiated communication it will not resume heating to that temperature without physical interaction. So it's all rs485 or nothing. To turn the heater on in pool mode you send 68 0c e9. That's device 104, command 12, argument 233. (I think there are other command variations in the 0c range but I haven't explored further since i found what i needed). Spa mode is on is 68 0c ee. To continue heating you have to KEEP sending it commands at least every 20 seconds or the heater turns back off. Now that I think about it I'm sure there are 0c commands to turn the heater off without timing out and I'll track them down next. It responds with an 0d 08 if the heater is on or 0d 10 if the heater is off.

So, how do you know when to turn the heater on or off? You have to ping the temperature sensor using the 0x25 block. I've been sending it 68 25 00 but it may not need an argument. It responds with a 7 byte message. 25 33 xx 04 f7 f5 yy. The yy value is the temperature in Farenheit with 20 added for reasons. So 85 degrees would be 105, or 0x69 . 80 degrees = 100 / 64. The xx byte seems to be incrementing slowly except when my heater sat idle, so I presume it is the lifetime hours the heater has been active or maybe amount of gas used. None of the other values change so I have no info on them yet and they may just be filler. Regardless, I don't need any more info to write a controller, I just needed a way to check the current temperature and turn the heat on or off.

To summarize here's the logic loop the power center uses:
1) ping for unit presence about every 10 seconds: 68 00 00
2) get the temperature: 68 25 00
3) if the pool is below whatever the aqualink temperature is set to then also send a heater on command: 68 0c e9
4) ill bet there's a heater off command xx once if temperature is equal or above the set point. 68 0c xx .. or just let it time out.

I'm sure there's extra code to keep the heat from cycling on and off too quickly, and perhaps different heat levels...but this is enough to get started.
I have a Pi Zero that will have a web/api control panel. It will need a hardware stop/start button outside the unit so I can hand over control to the built-in keypad if necessary, plus an air-temp sensor for science.
Phase 2 will be adding my Hayward pump to the slave chain so it will kick up the GPM when the heater is on.
I don't suppose anyone has investigated the control of a Hayward pump?
 
Last edited:
If you dont have any Pentair equipment, you are right that NodeJs poolcontroller will not do much for you. I use it in stand alone mode to control my Intelliflo pump. Works great for that. The pi then controls all the valves using relays. I also use a temp probe and relays to turn my heater on and off to control the temperature.

I set the panel temp to 102 (Which is the max I would want it). Then I measure the temp based on an input temp I have and cycle the heater on and off using the fireman switch. Works great. My heater does not accept 485 based commands.
 
  • Like
Reactions: MyAZPool
Katodude, is sounds like we had basically the same goals, sorry to hear your heater doesn't even play ball. I'm new to pool ownership and blindly had the contractor pick equipment.. I'm glad he got a heater that even had rs485 control.. and also glad it wasn't the complicated protocol that pentair seems to have!
 
Yep same goals I wanted to control all my pool equipment from a single Pi. I have achieved that. Plus now I am measuring both temp and pH. I have plans for a v2 of my controller based on some new Sequent Hats, but my laziness has kept me from doing it.

I was disappointed when I discovered my heater had no way to send it an RS-485 signal, but got over it pretty quick with my work around. It works great, I can control the temp from my phone while in the SPA.

When I built the first control box I had a Hayward pump and was going to replace it with the Hayward VSP (I think the 950 model). I did find the protocols out there, but then I just opted for an Intelliflo since the local Leslies had a sale on them.

From what I remember to control the Hayward it is similar to your heater where you have to send it a signal every second or the pump shuts off. This may help you or at least point you in the right direction.


At some point I am going to do some better Alexa integration, but not urgent yet as well. Laziness still has the better of me.
 
Holy Crud, that's perfect! I have a good feeling my pump uses the same codes since it looks very similar and this saves me so much hacking time!

I've decided to start from scratch on a web interface / rest api in django since i have a friend learning that framework i might help.. this may end up pretty closely tied to my exact set of equipment, but could still help some others down the line. I'll report back in when I have something posted to github.

Thanks for the link!
 
@rekliner : This looks like exactly I'm looking for, too! I have the same heater model and recently thought I could get AqualinkD setup to control it 🤦‍♂️ No dice.

I'm new here, but I have a raspberry pi and an RS-485 to USB cable connected to the Aqualink RS terminals of the heater's control board. The device enumerates as /dev/ttyUSB0 but just listening I don't get anything from it. Using `serial_logger` on it times out. I posted on their forums but was politely informed my setup (just a solo heater) is unsupported.

But what you've found by probing the heater is basically exactly what I want and might be enough for me to pull it into nodejs-PoolController. Any chance you've got some code I could take a look at and see if I can make work here?

Thank you!
 

Enjoying this content?

Support TFP with a donation.

Give Support
So I've attached the following python3 script which attempts to replicate the logic loop of the power center as documented by @rekliner above

I think the meaningful bit is here:
Python:
def control(s, ser, args):
    # Ping for unit presence
    print("{}: Sending 0x{} to heater".format(time.time(), PING_COMMAND.hex()), flush=True)
    ser.write(PING_COMMAND)
    print("{}: Waiting for unit presence response".format(time.time()), flush=True)
    response = ser.read()
    if len(response) > 0:
        print("{}: Read {} unit presence bytes: 0x{}".\
              format(time.time(), len(response), response.hex()), flush=True)
    else:
        print("{}: Heater unit not present!".format(time.time()), flush=True)
        s.enter(10, 1, control, (s, ser, args))
        return
             
    # Get the temperature
    ser.write(GET_TEMPERATURE_COMMAND)
    response = ser.read()
    temp_deg_f = args.set_point_deg_f
    if len(response) > 0:
        print("{}: Read {} temperature bytes: 0x{}".
              format(time.time(), len(response), response.hex()))
        temp_deg_f = int(response[6] - 20)
        print("{}: temp_deg_f is: {}".format(time.time(), int(temp_deg_f-20)))
    else:
        print("{}: Heater unit did not respond to temperature request!".format(time.time()))
        s.enter(10, 1, control, (s, ser, args))
        return
   
    low_point = args.set_point_deg_f - args.set_point_window_deg_f
    high_point = args.set_point_deg_f + args.set_point_window_deg_f
   
    if high_point < low_point:
        raise ValueError("Temperature hold window is illogical")
   
    if temp_deg_f < low_point:
        ser.write(HEATER_ON_COMMAND)
    elif temp_deg_f >= high_point:
        ser.write(HEATER_OFF_COMMAND)
       
    response=ser.read()
    if len(response) > 0:
        print("{}: Read {} post-command bytes: 0x{}".
              format(time.time(), len(response), response.hex()))
   
    s.enter(10, 1, control, (s, ser, args))

Of course, when I try to execute it, I get nothing. :-(
I'm using the Aqualinkd recommended USB->RS-485 adapter and have the wires correctly plugged into the heater's RS-485 header. The code to open the serial port looks like this:
Python:
    # According to: http://doityourselfchristmas.com/forums/archive/index.php/t-14596.html?s=ae1a73bb141ff476470ef72d8725fe06
    # Serial port runs at 9600 baud, no parity, 1 stop bit, 7-bit chars only.
    # DTR must be HIGH. RTS must be LOW to receive and HIGH to transmit!
    print("Opening rs485 serial connect on port {}".format(args.port), flush=True)
    ser = serial.rs485.RS485(port=args.port,
                             baudrate=9600,
                             bytesize=serial.SEVENBITS,
                             parity=serial.PARITY_NONE,
                             stopbits=serial.STOPBITS_ONE,
                             timeout=1,
                             rtscts=True)
    ser.rs485_mode = serial.rs485.RS485Settings(
                             rts_level_for_tx=True,
                             rts_level_for_rx=False,
                             loopback=False,
                             delay_before_tx=None,
                             delay_before_rx=None)
    if not ser.is_open:
        raise RuntimeError("Failed to open serial port {}".format(args.port))

When I execute the script I get:
Code:
Opening rs485 serial connect on port /dev/ttyUSB0
1610663060.0310533: Sending 0x680000 to heater
1610663246.555162: Waiting for unit presence response
1610663246.555546: Read 1 unit presence bytes: 0x00

Any advise on how to proceed? It's taking around 200 seconds to send 3 bytes. I don't think I have the serial connection set up correctly, but I'm not sure where I'm wrong or how to debug. How did you "probe" it?

Thanks!
 

Attachments

  • jandy_heater_control.py.txt
    4.3 KB · Views: 8
  • Like
Reactions: MyAZPool
Start at the heater and work back towards the Pi. Your USB to RS485 cable has TX and RX lights, right? If you have it wired properly and the heater is broadcasting packets, you should see the RX light blinking. RS485 uses differential signalling, so you should only need two wires connected to your adapter. Tying the adapter ground or Vcc to the heater may be skewing the bias voltages, preventing communication. Try disconnecting them and see if that helps. You may find disconnecting only the Vcc works (3 wire connection).
 
Thanks for the response! And the suggestions! I will try them!
> the heater is broadcasting packets
Do you know if these jandy heaters are just always broadcasting packets on the RS-485 bus? That's what I was kind of hoping but from reading online now I'm not sure if it takes a command to be sent from the panel in order to kick them into "Aqualink RS" mode, at which point they just respond to messages. That is, how confident are you these should be broadcasting packets in the steady state?

Thanks again!
 
I know zero about the Jandy heater. The closest experience I have is I have my Jandy RS panel connected to AquaLinkD, and the panel continuously spits out messages. My heater is a Hayward, but it's not hooked up to the panel at all and has to be controlled manually. I'm still learning how all this stuff works, just inherited my first pool with the house we bought in October. I learned more about my panel by hooking up AquaLinkD than I got from the previous owners lol.
 
Ah, got it, thanks! I'm trying to do this without shelling out for the RS panel thing. But I agree from my reading that the panel is regularly broadcasting messages.

I'll still give your suggestions on watching the USB-RS485 adapter lights and trying 2 or 3 wires instead of all four a shot! Thanks!
 
Okay, so made a little bit of progress on this last night, thanks to @superuser for the suggestion. After I tried disconnecting the VCC (Red) and, then, also the Ground(Black) wires, I still got nothing. Then I had a thought that maybe RS-485 wires aren't color coded. So, maybe Jandy/Zodiac/Whoever is using Yellow/Green for RS-485 differential Data+ Data- but maybe my random Amazon RS-485 to USB adapter is not? For that matter, maybe Red isn't VCC and Black isn't Ground? I looked up the Amazon adapter and someone helpfully had put in comments what the color codes on the adapter's wires were and, sure enough, green is not Data+ or Data-.

At this point I formulated a plan of attack that involved combinatorics (ha! Just trying every combination of wire colors and positions) and intuition. Eventually, I landed upon:
PXL_20210118_215213820.jpg
In an attempt to document what I believe I've discovered, if you look at the connector above, here is what each numbered pin/colored pair actually are if my source data is correct:
1. RED: Empty (but I assume VCC)
2. BLK: GND
3. YEL: RS-485 Data-
4. GRN: RS-485 Data+

I'm not 100% certain on this, but it's the only way I've been able to get any reliable messages from the heater, and I tried a few other things too just to see. My script from above still does nothing, but if I use serial_logger from the AqualinkD package, I get:
Code:
root@raspberrypi:/home/pi/software/AqualinkD/release# ./serial_logger /dev/ttyUSB0 -n -d -r
AqualinkD serial_logger V1.4
Debug:   RS Serial: Openeded serial port /dev/ttyUSB0
Info:    RS Serial: Set serial port /dev/ttyUSB0 io blocking attributes
Notice:  RS Serial: Logging serial information!
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x00 of type   Unknown '0x81' | HEX: 0x10|0x02|0x00|0x81|0x21|0xd5|0x00|0x1c|0x00|0x00|0x2e|0x52|0x62|0x00|0x00|0x20|0xe6|0x00|0x00|0x00|0xa2|0x8d|0x95|0x29|0x92|0x00|0x30|0x00|0x42|0x45|0x05|0x00|0x82|0x4c|0x08|0x02|0x00|0x41|0x00|0x00|0xfd|0x01|0x00|0x2a|0xb1|0x06|0x42|0x00|0x81|0x12|0x20|0xa0|0xe2|0x00|0x00|0x89|0x05|0x24|0x50|0x04|0x00|0x00|0x8e|0x00|0xc4|0x12|0x00|0x00|0x05|0x12|0x35|0x00|0x0c|0x14|0x02|0x2d|0x60|0x00|0x42|0x63|0x18|0x30|0x00|0x38|0x8d|0x00|0x1b|0x44|0x00|0x52|0x08|0x38|0x20|0x00|0x10|0xab|0x22|0x8a|0x00|0x00|0x2d|0xe3|0x22|0x13|0x00|0x00|0x31|0x29|0x01|0x84|0x10|0x00|0x90|0x04|0x00|0x0b|0x16|0x12|0x0a|0x11|0x00|0x5c|0xa8|0xc0|0x95|0x01|0x00|0xe2|
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x02 of type   Unknown '0x19' | HEX: 0x10|0x02|0x02|0x19|0x52|0x00|0xc5|0x08|0x20|0x80|0x89|0x00|0x60|0x09|0x24|0x83|0x04|0x00|0xea|0x10|0xb2|0x05|0x00|0x5c|0x01|0x02|0x1a|0x62|0x04|0x00|0x52|0x00|0x08|0x82|0x22|0x5c|0x00|0x00|0x00|0x94|0x04|0x24|0x00|0x80|0x84|0x00|0x9a|0x09|0x00|0x05|0x08|0x00|0x00|0x85|0xa0|0xd0|0x4e|0x0a|0x00|0x52|0x34|0xa1|0x4a|0x00|0x00|0x70|0x00|0x00|0x02|0x07|0x00|0x8d|0x02|0x80|0xf0|0x00|0x00|0xfe|0x00|0x80|0x33|0x02|0x00|0x4d|0xa2|0x14|0x85|0xea|0x00|0x00|0x50|0x10|0xc0|0xa1|0x00|0xda|0x81|0x3d|0x89|0x00|0x00|0xc2|0x33|0x01|0x40|0x00|0x00|0x95|0x10|0x08|0x8d|0x00|0x00|0x02|0x03|0x5a|0xe0|0x00|0x00|0x98|0x80|0x69|0x03|0x00|0x49|0x47|0x02|0x29|
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x00 of type   RSSA DevStatus | HEX: 0x10|0x02|0x00|0x13|0x80|0x81|0x60|0x24|0x01|0x00|0x99|0x28|0x12|0x00|0x14|0x00|0x5c|0x08|0x12|0x04|0x88|0x00|0x08|0x9e|0x31|0x22|0x00|0xc2|0xc0|0x62|0x60|0x07|0x00|0x28|0xe2|0x52|0xa6|0x00|0x1a|0x12|0x08|0x08|0x00|0x29|0x12|0xe2|0x91|0x02|0x00|0x22|0x49|0x81|0x00|0x42|0x00|0x33|0x05|0x00|0x22|0x41|0x12|0x80|0x52|0x00|0x95|0xc1|0x0c|0x02|0x00|0x21|0x08|0x00|0x12|0x25|0x00|0xe2|0x38|0x00|0x00|0x40|0x62|0x80|0x82|0x09|0x00|0x59|0xe7|0x1d|0x00|0x00|0xa5|0x40|0x20|0x66|0x00|0x00|0x20|0x35|0x02|0x89|0x10|0x04|0x09|0x00|0x00|0x00|0x35|0x18|0x2e|0x20|0x8f|0x00|0x10|0x17|0xb4|0x40|0x5a|0x00|0x08|0x06|0x14|0x00|0x40|0x02|0x82|0x82|0x00|0x00|
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x02 of type            Probe | HEX: 0x10|0x02|0x02|0x00|0x00|0x82|0x0c|0xa4|0x70|0x00|0xc0|0x40|0xc2|0x21|0x40|0x00|0x85|0xc8|0x8a|0x19|0x00|0x00|0x00|0x09|0x92|0xff|0x00|0x2c|0x0a|0x12|0x00|0x00|0x21|0x10|0x10|0x02|0x60|0x00|0x00|0x10|0xd2|0x21|0xa1|0x40|0x00|0x24|0x20|0x00|0x30|0x02|0x00|0x14|0x80|0x03|0x41|0x00|0x00|0x42|0x80|0x60|0x90|0x05|0x00|0xa2|0x02|0x62|0x08|0x00|0x20|0x25|0xe0|0xca|0x71|0x00|0x01|0xa6|0x01|0x00|0xc4|0x40|0x88|0x02|0x0e|0x00|0xf5|0x18|0x68|0x90|0x02|0x00|0x02|0x11|0x00|0x60|0x1d|0x00|0xa6|0x00|0xc2|0x20|0x00|0x40|0x0b|0x0e|0x80|0x00|0x00|0x14|0x80|0x45|0x00|0x00|0x10|0x04|0x09|0x84|0x02|0x00|0x29|0x45|0x44|0x09|0x00|0x02|0x0c|0x00|0x00|0x00|
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x02 of type            Probe | HEX: 0x10|0x02|0x02|0x00|0xd4|0x15|0x0c|0x00|0x01|0x00|0x2e|0x08|0x09|0x22|0x30|0x00|0x00|0xe6|0x80|0x08|0x72|0x00|0x00|0x08|0xa8|0x04|0x88|0x00|0x00|0x00|0x29|0x00|0x03|0x00|0xa2|0x01|0x82|0x10|0x05|0x00|0xe2|0x05|0x90|0x03|0x00|0x64|0x45|0x11|0x2c|0x0b|0x00|0x84|0x5b|0x22|0x61|0x00|0x00|0x10|0x48|0x80|0x10|0x40|0x00|0x14|0x00|0x88|0x22|0x06|0x00|0x40|0xfe|0x00|0x00|0x0c|0x00|0xf8|0x85|0x1d|0x08|0x02|0x00|0x4c|0x3c|0x95|0x01|0x80|0x00|0xe0|0x98|0x2c|0x68|0x00|0x00|0xb0|0x02|0x83|0x00|0x09|0xc5|0x62|0x00|0x49|0x00|0x00|0x10|0x40|0x02|0x82|0x80|0x00|0x41|0x28|0x01|0x00|0x00|0x10|0x24|0x40|0x80|0x22|0x00|0x00|0xc0|0x8a|0x05|0x00|0x06|0x64|
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x04 of type   Unknown '0x20' | HEX: 0x10|0x02|0x04|0x20|0x48|0x00|0x20|0x26|0x11|0x48|0x00|0x00|0x20|0xe6|0x00|0x50|0x21|0x00|0x18|0x00|0x21|0x01|0x00|0x02|0x0c|0x0a|0x06|0x13|0x00|0x1d|0x80|0xd2|0x04|0x00|0x00|0x90|0x16|0x34|0xf2|0x00|0x00|0x39|0x30|0x12|0x20|0x00|0x00|0x4d|0x0c|0x00|0xd0|0x42|0x00|0xf2|0x12|0x48|0xa0|0x05|0x00|0x92|0xa2|0x00|0x43|0x00|0x00|0x28|0x92|0x00|0x12|0x01|0x38|0xc0|0x01|0x00|0x10|0x02|0x05|0xa0|0x00|0x41|0x10|0x7c|0xc0|0x09|0x00|0x7a|0xb1|0x2d|0x20|0x1c|0x00|0x00|0x01|0x11|0x8c|0x02|0x00|0x80|0x10|0x29|0x0a|0x32|0x00|0xe2|0x85|0x22|0x41|0x11|0x00|0x42|0x28|0x66|0xe6|0x1a|0x00|0x28|0x04|0x10|0x10|0x00|0x72|0x52|0x80|0x38|0x00|0x58|0x60|0x42|
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x8d of type   Unknown '0x60' | HEX: 0x10|0x02|0x8d|0x60|0x09|0x00|0x1d|0x81|0x05|0x09|0x00|0x40|0x80|0x50|0xc4|0x05|0x00|0xb8|0x24|0x05|0x29|0x00|0x85|0x20|0x05|0x45|0x02|0x00|0x44|0x3e|0xc0|0x15|0x00|0x00|0x42|0x01|0x00|0x00|0x00|0x2e|0x16|0x16|0x18|0x00|0x00|0x04|0x09|0x20|0xa2|0x15|0x00|0x98|0x1b|0x20|0x00|0x00|0x24|0x02|0x00|0x90|0x09|0x00|0x00|0x32|0x2a|0xf1|0x00|0x00|0x16|0x00|0x00|0x20|0x24|0x00|0x81|0x02|0x0b|0x90|0x00|0x25|0x4d|0x20|0x08|0x2b|0x00|0x51|0x53|0x18|0x00|0x50|0x04|0x46|0x51|0x02|0x00|0x0d|0x28|0x80|0xf0|0x00|0x22|0x40|0x09|0x20|0x05|0x00|0x00|0x8a|0xc9|0x5e|0x12|0x00|0x00|0x10|0xdb|0x34|0x50|0x00|0x00|0x05|0x90|0x81|0x00|0x00|0x00|0x20|0xc0|0x01|
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x00 of type   Unknown '0x21' | HEX: 0x10|0x02|0x00|0x21|0x14|0x88|0x04|0x04|0x00|0x50|0x20|0x08|0x00|0x00|0x00|0x4a|0x00|0x00|0x46|0x09|0x01|0x00|0x05|0x00|0x88|0x00|0x00|0xe2|0x40|0x48|0x91|0x01|0x00|0x4a|0x90|0x02|0xbe|0x00|0x00|0x08|0x8a|0x2a|0x60|0x71|0x00|0x00|0xcc|0x12|0x02|0x0c|0x00|0x20|0x03|0x2b|0x3a|0x00|0x00|0x04|0x04|0xad|0xb0|0x00|0x42|0x28|0x00|0x4a|0x02|0x00|0x82|0x05|0x02|0x81|0x09|0x00|0x05|0x00|0xd8|0xc0|0x00|0x00|0x22|0x28|0x29|0x0c|0x00|0x00|0x20|0xc4|0x00|0x08|0x81|0x00|0x20|0x21|0x00|0x08|0x22|0x42|0x05|0x69|0x00|0xa0|0x64|0x01|0x88|0x85|0x00|0x05|0x2c|0x84|0x81|0x29|0x00|0x0c|0x45|0x51|0x39|0x04|0x00|0x2e|0x10|0xc9|0xb0|0x01|0x00|0x09|0x84|0x7f|
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x00 of type   Unknown '0x60' | HEX: 0x10|0x02|0x00|0x60|0x88|0x80|0x00|0x08|0x48|0x25|0x35|0x02|0x00|0x00|0x00|0x0d|0x00|0x02|0x00|0x60|0x02|0x10|0x21|0x00|0x00|0x13|0x43|0x0c|0xf4|0x00|0x19|0x41|0x28|0x99|0x00|0x01|0x00|0x46|0x40|0xa0|0x00|0x04|0x00|0x60|0x01|0x83|0x82|0x20|0x00|0x54|0x19|0xe2|0x02|0x00|0x0c|0x44|0x20|0x84|0x00|0x00|0x08|0x21|0x74|0x29|0x00|0x00|0x00|0x03|0x50|0x06|0xaa|0x00|0x42|0x2e|0x05|0x04|0x00|0x00|0xc2|0x02|0xc2|0x4f|0x00|0x00|0x41|0x50|0x82|0x00|0x00|0x02|0x00|0xc4|0x07|0x28|0x00|0x12|0x0c|0xe0|0x14|0x04|0x00|0x85|0x21|0x04|0x4e|0x02|0x00|0x07|0x00|0xc2|0x83|0x00|0x22|0x30|0x00|0x24|0x0a|0x00|0x50|0x85|0x09|0x49|0x00|0x00|0xc2|0x02|0x00|0x50|
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x94 of type      Lng Message | HEX: 0x10|0x02|0x94|0x04|0x00|0xf0|0x12|0x8d|0x00|0x00|0x00|0x44|0x88|0x42|0x60|0x86|0x00|0x75|0x14|0xa3|0x40|0x00|0x00|0x00|0x25|0x21|0x26|0x10|0x02|0x01|0x12|0x21|0x00|0x12|0xcf|0x81|0x82|0x00|0x00|0xe2|0x0a|0x00|0x00|0x3c|0x00|0x00|0xb1|0x04|0x08|0xb0|0x00|0x00|0x22|0x62|0x0d|0xe0|0x06|0x00|0x20|0x00|0x11|0x21|0x90|0x00|0x80|0x81|0xd0|0x20|0x00|0x00|0xd4|0x00|0x01|0x15|0x0f|0x00|0x20|0x45|0x09|0x42|0x70|0x00|0x00|0x70|0xd3|0x18|0x00|0x70|0x40|0xa9|0x25|0x00|0x00|0x22|0x02|0xa6|0x60|0xc4|0x00|0x06|0x2c|0x80|0x90|0x00|0x00|0x12|0x81|0x60|0x39|0x00|0x09|0x42|0x42|0x00|0x00|0x84|0x20|0xe0|0xd4|0x00|0x01|0x94|0x00|0x20|0x01|0x00|0x28|0x08|
^CNotice:  RS Serial: Stopping!
Warning: RS Serial: Serial packet too large
Warning: RS Serial: BAD PACKET To 0x02 of type  PDA HlightChars | HEX: 0x10|0x02|0x02|0x10|0x02|0x00|0x04|0x05|0x00|0x14|0x0c|0x00|0x09|0xc0|0x64|0x20|0x0c|0x00|0x82|0x06|0x00|0x00|0xc0|0x00|0x81|0xc5|0x88|0x00|0x00|0x00|0x4a|0x00|0x80|0x06|0x0c|0x00|0xb9|0x78|0x81|0x00|0x00|0x45|0x80|0x10|0x20|0x01|0x00|0x01|0x06|0x58|0x05|0x00|0x05|0x02|0x00|0x8c|0x01|0x00|0x82|0x89|0x80|0x02|0x00|0x00|0x00|0x80|0x10|0xa2|0x84|0x00|0x05|0xf2|0x08|0x0c|0x00|0x10|0x04|0x30|0x00|0x04|0x00|0x08|0x0c|0x00|0x08|0x15|0x00|0x04|0x2a|0x00|0x21|0x80|0x22|0x84|0x00|0x60|0x08|0x90|0x15|0x00|0x00|0xc9|0x05|0x20|0xc0|0x00|0x00|0x07|0x10|0x2a|0xe0|0x85|0x00|0x00|0x80|0x72|0x0b|0x8c|0x00|0x00|0x42|0x00|0x04|0xc0|0x00|0x00|0xc2|0x90|
Debug:   RS Serial: 

Notice:  RS Serial: RS485 interface received 0 packets in 36 seconds (~0.00 Msg/Sec)
Notice:  RS Serial: Jandy Control Panel Model   : 
Notice:  RS Serial: Jandy Control Panel Version : 
Notice:  RS Serial: Jandy ID's found
Notice:  RS Serial:

So, this was pretty exciting after hours of just nothing. But now I'm stuck with how to reverse engineer this nonsense. I guess my first attempt will be to look at the source code for serial_logger and see if I can figure out how to get my script to read bytes in Python. But after that, I'm really unsure of what to do.

Would appreciate any and all thoughts or comments! Thanks for the help so far.
 
Nice work, it's usually the simplest things too. (y)

Just for grins, I enabled RS485 logging and pulled some data from the log. It looks similar in structure to what you're seeing so I would assume thus far that comms are good. Keep in mind my data is coming from an old panel in PDA mode.

Code:
To 0x60 of type        PDA Clear | HEX: 0x10|0x02|0x60|0x09|0x00|0x00|0x7b|0x10|0x03|
To 0x60 of type      Lng Message | HEX: 0x10|0x02|0x60|0x04|0x04|0x50|0x4f|0x4f|0x4c|0x20|0x4d|0x4f|0x44|0x45|0x20|0x20|0x20|0x20|0x20|0x4f|0x4e|0x36|0x10|0x03|
To 0x60 of type      Lng Message | HEX: 0x10|0x02|0x60|0x04|0x05|0x50|0x4f|0x4f|0x4c|0x20|0x48|0x45|0x41|0x54|0x45|0x52|0x20|0x20|0x4f|0x46|0x46|0xa9|0x10|0x03|
To 0x60 of type      Lng Message | HEX: 0x10|0x02|0x60|0x04|0x06|0x53|0x50|0x41|0x20|0x4d|0x4f|0x44|0x45|0x20|0x20|0x20|0x20|0x20|0x4f|0x46|0x46|0x20|0x10|0x03|
To 0x60 of type      Lng Message | HEX: 0x10|0x02|0x60|0x04|0x07|0x53|0x50|0x41|0x20|0x48|0x45|0x41|0x54|0x45|0x52|0x20|0x20|0x20|0x4f|0x46|0x46|0x75|0x10|0x03|
To 0x60 of type      Lng Message | HEX: 0x10|0x02|0x60|0x04|0x08|0x4d|0x45|0x4e|0x55|0x20|0x20|0x20|0x20|0x20|0x20|0x20|0x20|0x20|0x20|0x20|0x20|0x33|0x10|0x03|
To 0x60 of type      Lng Message | HEX: 0x10|0x02|0x60|0x04|0x09|0x45|0x51|0x55|0x49|0x50|0x4d|0x45|0x4e|0x54|0x20|0x4f|0x4e|0x2f|0x4f|0x46|0x46|0xfe|0x10|0x03|
To 0x60 of type       PDA Hlight | HEX: 0x10|0x02|0x60|0x08|0x08|0x00|0x00|0x82|0x10|0x03|
To 0x60 of type      Lng Message | HEX: 0x10|0x02|0x60|0x04|0x40|0x20|0x20|0x20|0x20|0x20|0x54|0x55|0x45|0x20|0x31|0x31|0x3a|0x34|0x36|0x41|0x4d|0xf8|0x10|0x03|
To 0x60 of type      Lng Message | HEX: 0x10|0x02|0x60|0x04|0x01|0x41|0x49|0x52|0x20|0x20|0x20|0x20|0x20|0x20|0x20|0x20|0x20|0x50|0x4f|0x4f|0x4c|0xad|0x10|0x03|
To 0x60 of type      Lng Message | HEX: 0x10|0x02|0x60|0x04|0x82|0x20|0x37|0x34|0x60|0x20|0x20|0x20|0x20|0x20|0x36|0x30|0x60|0x20|0x20|0x20|0x20|0xc9|0x10|0x03|
To 0x69 of type            Probe | HEX: 0x10|0x02|0x69|0x00|0x7b|0x10|0x03|
To 0x6a of type            Probe | HEX: 0x10|0x02|0x6a|0x00|0x7c|0x10|0x03|
To 0x6b of type            Probe | HEX: 0x10|0x02|0x6b|0x00|0x7d|0x10|0x03|
To 0x38 of type            Probe | HEX: 0x10|0x02|0x38|0x00|0x4a|0x10|0x03|
To 0x39 of type            Probe | HEX: 0x10|0x02|0x39|0x00|0x4b|0x10|0x03|
To 0x3a of type            Probe | HEX: 0x10|0x02|0x3a|0x00|0x4c|0x10|0x03|
To 0x81 of type            Probe | HEX: 0x10|0x02|0x81|0x00|0x93|0x10|0x03|
To 0x3b of type            Probe | HEX: 0x10|0x02|0x3b|0x00|0x4d|0x10|0x03|
To 0x50 of type            Probe | HEX: 0x10|0x02|0x50|0x00|0x62|0x10|0x03|
To 0x60 of type           Status | HEX: 0x10|0x02|0x60|0x02|0x00|0x00|0x00|0x00|0x00|0x74|0x10|0x03|
To 0x60 of type       PDA Hlight | HEX: 0x10|0x02|0x60|0x08|0x09|0x00|0x00|0x83|0x10|0x03|
To 0x51 of type            Probe | HEX: 0x10|0x02|0x51|0x00|0x63|0x10|0x03|
To 0x60 of type           Status | HEX: 0x10|0x02|0x60|0x02|0x00|0x00|0x00|0x00|0x00|0x74|0x10|0x03|
 
Made a bit more progress on this. I'm now able to read the data from Python, which I was having trouble with before.
Unfortunately, the data does not make a ton of sense to me:
Code:
$ ./jandy_heater_control.py 
Opening rs485 serial connect on port /dev/ttyUSB0
Read the next packet. It was 748 bytes, with dest 0x8
Read the next packet. It was 743 bytes, with dest 0x41
Read the next packet. It was 755 bytes, with dest 0x5
Read the next packet. It was 752 bytes, with dest 0x55
Read the next packet. It was 754 bytes, with dest 0x0
Read the next packet. It was 759 bytes, with dest 0x24
Read the next packet. It was 747 bytes, with dest 0x43
Read the next packet. It was 759 bytes, with dest 0x8
Read the next packet. It was 749 bytes, with dest 0x0
Read the next packet. It was 763 bytes, with dest 0x11
Read the next packet. It was 746 bytes, with dest 0x0
Read the next packet. It was 736 bytes, with dest 0x0
Read the next packet. It was 750 bytes, with dest 0x0
Read the next packet. It was 745 bytes, with dest 0x3
Read the next packet. It was 742 bytes, with dest 0x52
Read the next packet. It was 746 bytes, with dest 0x42
Read the next packet. It was 743 bytes, with dest 0x40
Read the next packet. It was 743 bytes, with dest 0x32
Read the next packet. It was 379 bytes, with dest 0x0
(and so on). Based on this (which I believe was taken down a bit ago for some reason), I'm really not sure my Jandy heater is adhering to this protocol at all. The protocol is basically packets of the form "DLE + STX + DEST + CMD + ARGS + CHECKSUM + DLE + ETX", but I don't know that I'm seeing that.
1. The packets I'm reading are just way too large (700+ bytes on average?), or impossibly small (like not even three bytes at times)
2. Why would it be sending things to SO MANY different destinations? I would think, if anything, it would just be sending to 0x0 at some regular interval?

Does anyone here know how I can dig into this further or where I might go next? Wondering if 3D printing a contraption that holds my raspberry pi + camera above the display, reads it with some off the shelf OCR and pushes the up/down buttons on command will be easier than digging in here....
 
Does anyone here know how I can dig into this further or where I might go next?
This is a long shot but I remember reading somewhere about a person trying to find codes for a Hayward heater (chlorinator?). They were getting sporadic gibberish (like yours...BUT your gibberish seems like semi-intelligent, kinda-repeatable gibberish...so probably different). It turns out that the Hayward device they were targeting operated at a different baud rate than the rest of the known Hayward equipment...probably addressing some legacy issue.

I don’t know anything about Jandy, but you might try a few different settings: baud rate = 4800/19200 (most likely candidate), #bits = 8, others?
 
FWIW, my setup is running at 9600bps:
Code:
root@aqualinkd:~# stty < /dev/ttyUSB0 
speed 9600 baud; line = 0;
min = 0; time = 255;
-brkint -icrnl -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke
 

Enjoying this content?

Support TFP with a donation.

Give Support
Thread Status
Hello , This thread has been inactive for over 60 days. New postings here are unlikely to be seen or responded to by other members. For better visibility, consider Starting A New Thread.