tagyoureit / nodejs-Pentair

An application to read/write to Pentair pool controllers.
14 stars 6 forks source link

Some more bits for the wiki #3

Closed rflemming closed 7 years ago

rflemming commented 8 years ago

Not sure if you saw my followups to the previous issue, but I figured out a couple more things. Some of which related to the messages in the wiki you hadn't worked out entirely yet:

This message seems to occur as a response to a remote or wireless devices asking for this with:

[165, 10, 16, 32, 197, 1, 0, 1, 165]

It does also appear to pop up on its own occasionally. Not sure how regular it is.

165 - 10 - 15 - Broadcast 16 - Controller 5 - Cmd 8 - Length 15 - Hour 34 - Min 1 - Day of Week (Sun=1, Mon=2, Tue=4, Wed=8, Thu=16, Fri=32, Sat=64) 10 - Day 7 - Month 16 - Year (20XX) 0 - Clock Adjust (I'm guessing) 1 - DST (1=Auto, 0=Manual) 1 - Checksum High 47 - Checksum Low

165 - 10 - 15 - Broadcast 16 - Controller 8 - Cmd 13 - Length 85 - Temp1 85 - Temp2 73 - Air Temp 87 - Pool Setpoint 95 - Spa Setpoint 3 - Spa/Pool Heat Mode - 2 bits for each (0=Off, 1= Heater, 2=Solar Preferred, 3=Solar Only) 0 0 104 - Solar Temp 0 0 0 0 2 - Checksum High 247 - Checksum Low

The Spa/Pool Heat Mode byte above also corresponds to the 23rd byte in the main Controller -> Broadcast message. You'll see it switch from 3 to 15 below when I enabled solar for the Spa (which I don't actually have):

[165, 10, 15, 16, 2, 29, 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 32, 4, 84, 84, 32, 0, 74, 70, 0, 0, 3, 0, 0, 133, 183, 1, 13, 3, 223] [165, 10, 15, 16, 2, 29, 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 32, 4, 84, 84, 0, 0, 74, 70, 0, 0, 15, 0, 0, 120, 195, 1, 13, 3, 202]

Byte 17 also switches from 32 to 0 when Spa heat was enabled, so I'm not sure what's up with that.

I spent some time tinkering with bytes 26 and 27 and they hold some solar related configuration stuff, but it's honestly not all that important and I didn't quite work it all out yet. Toggles for things like Heat Pump, Solar Freeze Protection, and Night Cool.

tagyoureit commented 8 years ago

Awesome work. I'm traveling out of the country. Keep working on this and I'll be back next week and look more closely at what you have done and add it to the project. Tag

rflemming commented 8 years ago

Been doing a lot of tinkering. Everything I'm doing is based off mimicking the wireless remote since that's the only thing I have to test with. I've also just gone ahead and written my own code while screwing around with this because it was just easier for me that way. Here are some more random findings:

I've come to the conclusion that I'm better off just ignoring the messages between the pump and the controller. There's nothing there that can't be obtained directly from the controller if you know how to ask and the message formats remain consistent. So I'm only looking at messages with a 10 in the 2nd byte. Those appear to be controller related messages, while pump messages have a 0 there. Let's look at the CFI field (byte 4):

1 = Ack Message 2 = Controller Status 5 = Date/Time Status 8 = Heat/Temperature Status 11 = Circuit Names 23 = Pump Status 133 = Set Date/Time 134 = Set Circuit 136 = Set Heat/Temperature 138 = Set Custom Name 139 = Set Circuit Name 197: Get Date/Time 200: Get Heat/Temperature 202: Get Custom Name 203: Get Circuit Name 215: Get Pump Status

You might notice a little pattern above. There are 3 different messages that are all related. For example:

Heat/Temperature Status - 8 - 00001000 Set Heat/Temperature - 136 - 10001000 Get Heat/Temperature - 200 - 11001000

The same applies to Circuit Names and Date/Time. Custom Names, and Pump Status also follow the pattern, but I haven't seen all 3 types of messages first hand yet for those. So it could be that the CFI byte is really split in half. With the low order bits representing the message type and the high order bits designating if it is a status/get/set message.

Regarding circuit names. I pulled the list of built-in circuit names from the wireless manual. They too are listed in alphabetic order. It sorta kinda looks like there may be something to that, but it's not perfect. For example.

If I set AUX5 to be 'AIR BLOWER' I see: [165, 10, 16, 32, 139, 5, 7, 0, 2, 0, 0, 1, 120] Where 2 = AIR BLOWER alphabetically in the list.

If I change it back to AUX5 I see:

[165, 10, 16, 32, 139, 5, 7, 0, 7, 0, 0, 1, 125] Where 7 = AUX5 alphabetically in the list.

Unfortunately that doesn't always seem to match up at least on my system for every circuit. I need to look into that more. There are 101 names listed in the manual, not sure I'm inclined to map out each one right now.

I don't think I recall seeing you mention that ACK message that is sent after a set command. Here are a couple examples:

[165, 10, 16, 32, 139, 5, 7, 0, 7, 0, 0, 1, 125] [165, 10, 32, 16, 1, 1, 139, 1, 108]

[165, 10, 16, 32, 134, 2, 4, 1, 1, 108] [165, 10, 32, 16, 1, 1, 134, 1, 103]

Its CFI == 1 and it looks to have a 1 in the 6th byte and the 7th byte corresponds to the CFI number of the command being ACKed. I haven't seen anything I'd consider a NACK. If you're going to set something it seems like checking for an ACK seems prudent.

tagyoureit commented 8 years ago

This might be a long response!

This message seems to occur as a response to a remote or wireless devices asking for this with: [165, 10, 16, 32, 197, 1, 0, 1, 165]

Yes, see Initial request

1 - Day of Week (Sun=1, Mon=2, Tue=4, Wed=8, Thu=16, Fri=32, Sat=64)

Interesting, this seems different from the days of week for the schedules.

The Spa/Pool Heat Mode byte above also corresponds to the 23rd byte in the main Controller -> Broadcast message. You'll see it switch from 3 to 15 below when I enabled solar for the Spa (which I don't actually have):

Yes, I noticed this as well but only in one place, not both. I documented this in my code previously, but not anywhere else (including the console logs). Thanks! (My description) Pentair controller sends the pool and spa heat status as a 4 digit binary byte from 0000 (0) to 1111 (15). The left two (xx) is for the spa and the right two (xx) are for the pool. EG 1001 (9) would mean 10xx = 2 (Spa mode Solar Pref) and xx01 = 1 (Pool mode Heater)

Byte 17 also switches from 32 to 0 when Spa heat was enabled, so I'm not sure what's up with that. I spent some time tinkering with bytes 26 and 27 and they hold some solar related configuration stuff, but it's honestly not all that important and I didn't quite work it all out yet. Toggles for things like Heat Pump, Solar Freeze Protection, and Night Cool.

Hmmm, it could also be the solar sensor. I have solar and will put this on the to-do list!

Let's look at the CFI field (byte 4):

Nice! I added quite a bit to your list based off what I know (and don't know).

You might notice a little pattern above. There are 3 different messages that are all related. For example: Heat/Temperature Status - 8 - 00001000 Set Heat/Temperature - 136 - 10001000 Get Heat/Temperature - 200 - 11001000

Very cool! And helpful.

Regarding circuit names. I pulled the list of built-in circuit names from the wireless manual. They too are listed in alphabetic order. It sorta kinda looks like there may be something to that, but it's not perfect. For example. Unfortunately that doesn't always seem to match up at least on my system for every circuit. I need to look into that more. There are 101 names listed in the manual, not sure I'm inclined to map out each one right now.

There seems to be an extra layer in here somewhere. The names are all displayed in alphabetical order including custom names when looking at the configuration screens. So, yes, we need to figure out where/how the associations are being made.

I don't think I recall seeing you mention that ACK message that is sent after a set command. Here are a couple examples:

I've seen these, but haven't spent enough time on writing the commands back to the bus to worry about the ACK's yet. Now that I have a very basic UI where I can (very soon) turn on/off circuits, this will be coming very shortly.

I added you as a contributor to the project, so if you want to append/modify the wiki directly feel free. Going through the wiki to document everything has given me a fresh look at the messages. Will get back to the code shortly.

If you discovered anything in writing your own code that helps you to debug/decode the messages, I'll be happy to include it here if appropriate.

rflemming commented 8 years ago

I updated the wiki a tiny bit related to the various actions.

Robert

On Sun, Jul 17, 2016 at 8:38 PM tagyoureit notifications@github.com wrote:

This might be a long response!

This message seems to occur as a response to a remote or wireless devices asking for this with: [165, 10, 16, 32, 197, 1, 0, 1, 165]

Yes, see Initial request http://Configuration/#initial-request

1 - Day of Week (Sun=1, Mon=2, Tue=4, Wed=8, Thu=16, Fri=32, Sat=64)

Interesting, this seems different from the days of week for the schedules http://Configuration/#schedules.

The Spa/Pool Heat Mode byte above also corresponds to the 23rd byte in the main Controller -> Broadcast message. You'll see it switch from 3 to 15 below when I enabled solar for the Spa (which I don't actually have):

Yes, I noticed this as well but only in one place, not both. I documented this in my code previously, but not anywhere else (including the console logs). Thanks! (My description) Pentair controller sends the pool and spa heat status as a 4 digit binary byte from 0000 (0) to 1111 (15). The left two (xx) is for the spa and the right two (xx) are for the pool. EG 1001 (9) would mean 10xx = 2 (Spa mode Solar Pref) and xx01 = 1 (Pool mode Heater)

Byte 17 also switches from 32 to 0 when Spa heat was enabled, so I'm not sure what's up with that. I spent some time tinkering with bytes 26 and 27 and they hold some solar related configuration stuff, but it's honestly not all that important and I didn't quite work it all out yet. Toggles for things like Heat Pump, Solar Freeze Protection, and Night Cool.

Hmmm, it could also be the solar sensor. I have solar and will put this on the to-do list!

Let's look at the CFI field (byte 4):

Nice! I added http://Broadcast/#actions quite a bit to your list based off what I know (and don't know).

You might notice a little pattern above. There are 3 different messages that are all related. For example: Heat/Temperature Status - 8 - 00001000 Set Heat/Temperature - 136 - 10001000 Get Heat/Temperature - 200 - 11001000

Very cool! And helpful.

Regarding circuit names. I pulled the list of built-in circuit names from the wireless manual. They too are listed in alphabetic order. It sorta kinda looks like there may be something to that, but it's not perfect. For example. Unfortunately that doesn't always seem to match up at least on my system for every circuit. I need to look into that more. There are 101 names listed in the manual, not sure I'm inclined to map out each one right now.

There seems to be an extra layer in here somewhere. The names are all displayed in alphabetical order including custom names when looking at the configuration screens. So, yes, we need to figure out where/how the associations are being made.

I don't think I recall seeing you mention that ACK message that is sent after a set command. Here are a couple examples:

I've seen these, but haven't spent enough time on writing the commands back to the bus to worry about the ACK's yet. Now that I have a very basic UI where I can (very soon) turn on/off circuits, this will be coming very shortly.

I added you as a contributor to the project, so if you want to append/modify the wiki directly feel free. Going through the wiki to document everything has given me a fresh look at the messages. Will get back to the code shortly.

If you discovered anything in writing your own code that helps you to debug/decode the messages, I'll be happy to include it here if appropriate.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/tagyoureit/nodejs-Pentair/issues/3#issuecomment-233223830, or mute the thread https://github.com/notifications/unsubscribe-auth/AHeuuBy-i-kFYDB3pGnlEymbawiMKdoCks5qWvUwgaJpZM4JKAhT .

tagyoureit commented 8 years ago

Great, I saw that. I figured out a couple of things as well including the 101 preset (+10 custom) circut names and functions. This is really great for the overall code because now I can eliminate the manual entries in the config.json file.

I've hit a small issue with writing back to the bus (I can't get it to work). Are you able to help me out? I can set up a Slack channel or possibly Gitter or just email. I'm not sure if it is my code or my USB<-->Serial 485 adapter.

rflemming commented 8 years ago

What I've been working on is in Python. I actually hadn't tested any writes just yet either, so thanks for making me get around to that :) I do have it working. My first question would be, are you including the preamble in the command? For example, a request for the date time message would look like this:

[255, 0, 255, 165, 10, 16, 32, 197, 1, 0, 1, 165]

If that's not it, then I'm not sure. Wiring is certainly a possibility.

As for the code I've been working on, I was really wanted to make something that was more of a library, with several modular components. So I'm working on something like a virtual wireless controller, that you can interact with and dump the state out of. This way you can throw whatever kind of serving method and data format you want in front of it. Each message type is handled by it's own class, so as I learn new fields or want to add things, I don't need to muck with anything else. At the end of the day I'll be tying it into my SmartThings setup. If I was smart I would have just stopped with the functionality I needed, but of course that's just too easy :)

Robert

On Sat, Jul 23, 2016 at 7:39 AM tagyoureit notifications@github.com wrote:

Great, I saw that. I figured out a couple of things as well including the 101 preset (+10 custom) circut names http://../Circuit-Names and functions. This is really great for the overall code because now I can eliminate the manual entries in the config.json file.

I've hit a small issue with writing back to the bus (I can't get it to work). Are you able to help me out? I can set up a Slack channel or possibly Gitter or just email. I'm not sure if it is my code or my USB<-->Serial 485 adapter.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/tagyoureit/nodejs-Pentair/issues/3#issuecomment-234721570, or mute the thread https://github.com/notifications/unsubscribe-auth/AHeuuMmGg7VhWv7CFIZiFZLD6m0g8YATks5qYieagaJpZM4JKAhT .

tagyoureit commented 8 years ago

Here's a test piece of code I've tried:

'use strict';

var serialport = require('serialport');
//var port = new SerialPort('/dev/cu.Cubelet-RGB');

var port = new serialport.SerialPort("/dev/ttyUSB0", {
    baudrate: 9600,
    databits: 8,
    parity: 'none',
    stopBits: 1,
    flowControl: false,
    parser: serialport.parsers.raw
});

port.on('open', function() {
 // var largeMessage = new Buffer(1024 * 10).fill('!');
  console.log('Calling write');
  port.write([255, 0, 255, 165, 16, 34, 134, 2, 9, 1, 1, 115], function() {
    console.log('Write callback returned');
    // At this point, data may still be buffered and not sent out over the port yet
    // write function returns asynchronously even on the system level.
    console.log('Calling drain');
    port.drain(function() {
      console.log('Drain callback returned');

    });
  });
});

port.on('data', function(data) {
  console.log('Received: \t', data);
});

Can you try this and see if it works? Since I'm not inside my main code, I was using this command to turn on circuit 9 so I could see if the controller responds.

My questions, if you could help me... 1) Does this work on your system? 2) Is your RS-485 setup for loopback? Meaning, if you write to the bus do you also see your commands appear as if they were coming from some other equipment? 3) Can you send me a piece of similar test Python code? 4) What cable are you using?

I'm trying to determine if it is my cable. I don't think the wiring is wrong or I wouldn't be able to read anything. Am I mistaken there? Could I be able to read but not write? I only have the Data+, Data- and ground wires attached.

This is the code from Michael Usner

var sendCommand = function(sFeature, sState, callback) {
    iFeature = featureStr[sFeature]
    iState = stateStr[sState]
    logger.info("Setting " + sFeature + " to " + sState)
    if (sFeature == "poolTemp") {
        packet = [00, 255, 165, 31, ctrl.MAIN, ctrl.REMOTE, 134, 4, sState, 97, 5, 0]
    } else {
        packet = [00, 255, 165, 31, ctrl.MAIN, ctrl.REMOTE, 134, 2, iFeature, iState]
    }
    checksum = 0
    for (var i=2; i < packet.length; i++) {
        checksum += packet[i]
    }
    packet.push(checksum >> 8)
    packet.push(checksum & 0xFF)
    logger.info("Sending " + packet)
    serialPort.write(packet, function (err, bytesWritten) {
        logger.info("Wrote " + bytesWritten + " bytes to the serial port")
        eventEmitter.once('poolStatus', function (obj) {
            return callback(err=null, obj={
                "time": obj.time,
                "waterTemp": obj.waterTemp,
                "airTemp": obj.airTemp,
                "pool": obj.pool,
                "spa": obj.spa,
                "blower": obj.blower,
                "poolLight": obj.poolLight,
                "spaLight": obj.spaLight,
                "cleaner": obj.cleaner,
                "spillway": obj.spillway,
                "waterFeature": obj.waterFeature
            })            
        });
    });
}

so I don't know why mine wouldn't work unless it is something with the cable??? I'm a little stumped here so much appreciate your help, again!

Also, I think we are heading down the same path. I may not be writing my code very modular-ly, but I do want to have both a responsive (websockets) webpage that can be used, but also expose a set of API's (REST URL's) so that I can later write a simple module and integrate with other home automation.

Is your SmartThings reliable? I'm pretty fed up with the Siri/HomeKit solution so looking to try Amazon Echo, SmartThings or something else (my buddy swears by Insteon) that is more reliable.

thx, Russ

rflemming commented 8 years ago

On Sun, Jul 24, 2016 at 11:00 AM tagyoureit notifications@github.com wrote:

1) Does this work on your system?

It does.

2) Is your RS-485 setup for loopback? Meaning, if you write to the bus do you also see your commands appear as if they were coming from some other equipment?

It is not.

3) Can you send me a piece of similar test Python code?

Here's the bare minimum for toggling a circuit on, in my case my pool light:

import serial

s = serial.Serial('/dev/ttyUSB0', 9600, 8, 'N', 1) s.write(bytearray([255, 0, 255, 165, 10, 16, 34, 134, 2, 4, 1, 1, 110])) s.close()

4) What cable are you using?

I'm using one of these:

https://www.amazon.com/gp/product/B00NKAJGZM

Then I just ran a pair of wires from it to the panel.

I'm trying to determine if it is my cable http://www.amazon.com/EZSync-RS485-USB-RS485-WE-compatible-EZSync010/dp/B010KJSCR8?ie=UTF8&psc=1&redirect=true&ref_=oh_aui_detailpage_o01_s00. I don't think the wiring is wrong or I wouldn't be able to read anything. Am I mistaken there? Could I be able to read but not write? I only have the Data+, Data- and ground wires attached.

Since Rx and Tx happen on different wires, it's entirely possible to receive and not be able to send.

so I don't know why mine wouldn't work unless it is something with the

cable??? I'm a little stumped here so much appreciate your help, again!

Seems like a wiring or cable problem of some kind to me.

Is your SmathThings reliable? I'm pretty fed up with the Siri/HomeKit

solution so looking to try Amazon Echo, SmarthThings or something else (my buddy swears by Insteon) that is more reliable.

I think SmartThings is the least shitty option, but it certainly can suck at times. There have certainly been a lot of problems with the platform in the past. I think it's better now, but there are still times when expected things don't happen quite right. Despite the promises they made with the version 2 hub, there are still a ton of basic things that require cloud connectivity to work, which can be a big downside. The good news is that there's a pretty large developer community, so there are lots of devices supported and lots of different apps already out there. The guy who used to run the Alexa project at Amazon just joined Samsung to be in charge of SmartThings, so it at least appears like Samsung is into furthering the platform.

Robert

tagyoureit commented 8 years ago

Thanks very much for testing/confirming. For $6 I ordered the same adapter you have.

Since Rx and Tx happen on different wires, it's entirely possible to receive and not be able to send. Very true. Since there is only Data+ and Data- I hope it is a hardware error and not user installation error! I'll double check when I get back to the project later today.

tagyoureit commented 8 years ago

My new USB adapter came (the one that you use) and I eagerly plugged it in and started my app. No data was being read so I swapped the Data+ and Data- wires. Started the app again and it was reading great just like last time. Triggered my write code and... NOTHING! Tried my test code. Same result. Tried your Python code and still nothing. Now I'm wondering if it is a system setting. Can you please post the output of the command stty -a -F /dev/ttyUSB0? Maybe something is messed up here on my end. If I can't figure that out, I think my remaining options are:

a) Now that I have 2 adapters, plug one into the other and actually see if it is writing b) reset my Raspberry Pi in case something is messed up at the O/S level c) install everything on either my Mac or Windows laptop and try it out there.

This seems to rule out a bad cable/adapter, anyway. Quite disappointing that it didn't work straight away. Trying to stay positive since I'm one step closer to a solution!

rflemming commented 8 years ago

Not sure what's up with that. Here's the info on my serial port:

speed 9600 baud; rows 0; columns 0; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; -parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts -ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke

Here's how mine is wired just to convirm: D-(B) goes to green on the panel and D+(A) goes to Yellow. I actually have it wired up to the terminals on the wireless antenna rather than the panel itself, but that shouldn't matter.

Robert

tagyoureit commented 8 years ago

Ok, I got it working... back to coding. How are you handling the ACK? Are you just going to send off the message and hope for the best? Or do some error checking? I'm thinking promises might answer the question but need to research it more. Also, because I'm getting so much information back now I'm going to convert the arrays to objects. I'll post more code in a few days, hopefully (although not sure how helpful my code is to you, anymore).

rflemming commented 8 years ago

In an asynchronous world promises are likely the way to go. While there is support for async stuff in python but that's not what I've used so far. I've just been using threads and queues and such to pass messages around. I'm not quite sure what I'm going to do just yet, I mocked something out on the plane (I'm traveling for work this week) but I haven't tested it. There are two cases in which I'd like to create blocking events. One is a set command where I'm expecting an Ack in return and the other are the query commands where I'm expecting a particular response. What I'm thinking of doing is having a callback associated with write commands, and tie that callback to a particular CFI number. This way as I'm servicing the message queue I can look for replies destined for my virtual controller and check to see if there are any registered callbacks for that particular CFI and run that accordingly (rather than simply updating the state). Then on writes, I can just block waiting for the callback to occur (which I can keep track of with a threading event, lock, etc. This is also where any retry code would be, just in case a message is lost or mangled in transit. The reality is I'm constantly refactoring things as I go along because I'm still not exactly sure how I want the end result to work :)

tagyoureit commented 8 years ago

Hey Robert, Just published a new version and there are a ton of changes including, and finally, writing back to the bus. I figured out a pretty good way to include write functionality in the current read code without needing to go the promises route. I can go into more detail, if you haven't found a good method yourself, but essentially anytime I want to write a message (either from the UI, API or retrieving the configuration at the beginning of the code) I put the messages into a queue. I write the message from the queue to the serial bus (but keep the message in the queue). The hook is that when inbound messages are received I check to see if the queue length is >0. If it is, I pass the incoming message back to another function that evaluates if it is the response to the message that was sent. The incoming messages are evaluated and if 5* messages come through without a correct ACK then I resend the message. If I do find a response, then I shift the array and call the write function to send the next command in the queue. This completely avoids any blocking code, promises or callbacks from the write code.

*I'm pretty sure this isn't the most efficient way of evaluating this. Because of the async nature of the calls, I originally tried the logic along the lines of "if the next incoming message isn't a response, then rewrite to the bus" but because I might be evaluating incoming messages at the same time as I'm writing I ran into the scenario where an ACK would be received but it wouldn't be the "next" message in the queue. Since there are so many messages flying around the arbitrary count of 5 messages isn't really a huge delay.