Raspberry Pi ClimateMaster Logger

In order for others to be able to use this code, I am posting it here until I get my act together to make it more robust and pop it onto GitHub.

The ClimateMaster DXM2 control has a lot of data that can be used to troubleshoot or know how your system is running. As an example, my HVAC system now has a graphical display telling us when its operating at various stages (purple line), and includes the drop/rise in water temperature from the ground loops (Light blue line):

This project uses a Raspberry Pi 4 and a Waveshare RS485 board https://www.waveshare.com/wiki/RS485_RS232_HAT.

The ClimateMaster DXM2 control board has two headers (P5 and P4) shown marked in green exposing an RS485 bus. These headers are used for a tech tool and or a communicating (but limited) thermostat. To use the bus, it must be switched to “slave” mode by ensuring dipswitch 3-1 is OFF as shown with the red dot. If you have nothing else connected to the P4/P5 headers this is a non issue. You need no other connections between these boards other than the A and B wires shown in blue below:

#!/usr/bin/python
# -*- coding:utf-8 -*-
# Raspberry Pi Climatemaster logging code.
# 2022 - Jeremy Laurenson
# Based on Waveshare RS485 Hat and Pi 4
# https://www.waveshare.com/wiki/RS485_RS232_HAT
import serial
import os
import sys
import logging
import urllib.request


logging.basicConfig(level=logging.INFO)
libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib')
if os.path.exists(libdir):
    sys.path.append(libdir)
    
import RPi.GPIO as GPIO
import time
from waveshare_RS485_RS232_HAT import config

RS485EN = 22
print("Connecting to serial...")
ser = config.config(Baudrate = 9600 , dev = "/dev/ttySC0")
data = ''
GPIO.output(RS485EN, GPIO.LOW) 
time.sleep(0.5)#Waiting to send
print("Climatemaster DXM2 data logger.")

cmd_lt1=bytearray([0x30,0x03,0x51,0x20,0x00,0x01,0x91,0x1D])
cmd_lt2=bytearray([0x30,0x03,0x51,0x21,0x00,0x01,0xC0,0xDD])
cmd_ewt=bytearray([0x30,0x03,0x51,0x23,0x00,0x01,0x61,0x1D])
cmd_lwt=bytearray([0x30,0x03,0x51,0x24,0x00,0x01,0xD0,0xDC])
cmd_lat=bytearray([0x30,0x03,0x51,0x25,0x00,0x01,0x81,0x1C])
cmd_bls=bytearray([0x30,0x03,0x51,0x28,0x00,0x01,0x10,0xDF])
cmd_dischargetemp =bytearray([0x30,0x03,0x51,0x27,0x00,0x01,0x20,0xDC])
cmd_pumpspeed     =bytearray([0x30,0x03,0x51,0x15,0x00,0x01,0x81,0x13])
cmd_targetairflow =bytearray([0x30,0x03,0x51,0x14,0x00,0x01,0xD0,0xD3])
cmd_controlvoltage=bytearray([0x30,0x03,0x51,0x2A,0x00,0x01,0xB1,0x1F])
cmd_getfaults     =bytearray([0x30,0x03,0x51,0x1C,0x00,0x03,0xD0,0xD0])
cmd_getserial     =bytearray([0x30,0x03,0x51,0x1F,0x00,0x01,0xA1,0x11])

def get_data(code):
    print("Getting ", code)
    GPIO.output(RS485EN, GPIO.LOW) 
    time.sleep(0.5)#Waiting to send

    ser.serial.write(code)
    time.sleep(0.2)
    GPIO.output(RS485EN, GPIO.HIGH)

    packetcount=7
    dataarray=[]
    try:
        while(packetcount>0):
            data_t = ser.serial.read(1)
            int_val = int.from_bytes(data_t, "big")
            dataarray.append(int_val)
            packetcount=packetcount-1
    except:
        dataarray=[]
    if len(dataarray)==7:
        if dataarray[0]==48:
            if dataarray[1]==3:
                if(dataarray[2]==2):
                    tempval=(dataarray[3]*255+dataarray[4])
                    return tempval
    return 0


def get_faults():
    GPIO.output(RS485EN, GPIO.LOW) 
    time.sleep(0.5)#Waiting to send

    ser.serial.write(cmd_getfaults)
    time.sleep(0.2)
    GPIO.output(RS485EN, GPIO.HIGH)

    packetcount=11
    dataarray=[]
    try:
        while(packetcount>0):
            data_t = ser.serial.read(1)
            int_val = int.from_bytes(data_t, "big")
            dataarray.append(int_val)
            packetcount=packetcount-1
    except:
        dataarray=[]
    if len(dataarray)==11:
        if dataarray[0]==48:
            if dataarray[1]==3:
                if dataarray[2]==6:
                    dataarray.pop(10)
                    dataarray.pop(9)
                    dataarray.pop(0)
                    dataarray.pop(0)
                    dataarray.pop(0)

                    return dataarray;
    return 0

def listToString(s):

    # initialize an empty string
    str1 = ""
 
    # traverse in the string
    for ele in s:
        str1 += str(ele)
        str1 += ":"
 
    # return string
    return str1


lt1=get_data(cmd_lt1)/10
lt2=get_data(cmd_lt2)/10
ewt=get_data(cmd_ewt)/10
lwt=get_data(cmd_lwt)/10
lat=get_data(cmd_lat)/10
taf=get_data(cmd_targetairflow)
bls=get_data(cmd_bls)
pspeed=get_data(cmd_pumpspeed)
cdt=get_data(cmd_dischargetemp)/10
cvol=get_data(cmd_controlvoltage)/10
fcodes=get_faults()

postcommand="http://10.0.1.10/environmentals/climatemaster.php?"
postcommand = postcommand + "lt1=" + str(lt1)
postcommand = postcommand + "&lt2=" + str(lt2)
postcommand = postcommand + "&ewt=" + str(ewt)
postcommand = postcommand + "&lwt=" + str(lwt)
postcommand = postcommand + "&lat=" + str(lat)
postcommand = postcommand + "&taf=" + str(taf)
postcommand = postcommand + "&bls=" + str(bls)
postcommand = postcommand + "&pspeed=" + str(pspeed)
postcommand = postcommand + "&cdt=" + str(cdt)
postcommand = postcommand + "&cvol=" + str(cvol)
postcommand = postcommand + "&fcodes=" + listToString(fcodes)

print(postcommand)


contents = urllib.request.urlopen(postcommand).read()
Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s