Windows basic .Net Code to control UPB

BobS0327

Active Member
If anyone's interested, I've attached Windows C# code that will allow you to control UPB lighting via a PIM.  You may have to change the COM port and possible the network id and then compile it. 
 
You'll have to enter the unit id and the state  (ON or OFF), that you want to put the unit into on the command line.  Right now, there's very little error checking.  If you do use it, I'd greatly appreciate a PM with any issues encountered.
 
 
 

using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Runtime.Serialization;
using System.IO.Ports;

class Program
{
    static byte[] cr = { 0x0D };
    static byte[] ctlt = { 0x14 };
    static byte[] ctlw = { 0x17 };
    static string COMPORT = "COM4";
    static string UPBString;
    static string UPBCommandPacket;
    // Do NOT change the value of the MESSAGEMODE variable. It puts PIM in the required Message Mode
    static string MESSAGEMODE = "70028E";
    const string NetworkID = "01";
    const string SourceID = "FF";
    static Dictionary<char, int> translate1 =
                  new Dictionary<char, int>();

    public static void Main(string[] args)
    {
        if (args == null || args.Length < 2)
        {
            Console.WriteLine("Aborting, invalid command line arguments entered");
            return;
        }
        build_dictionary();
        UPBCommandPacket = buildPacket(args[0].ToUpper(), args[1].ToUpper());
        SerialPort mySerialPort = new SerialPort(COMPORT);

        mySerialPort.BaudRate = 4800;
        mySerialPort.Parity = Parity.None;
        mySerialPort.StopBits = StopBits.One;
        mySerialPort.DataBits = 8;
        mySerialPort.Handshake = Handshake.None;
        mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
        mySerialPort.ReadTimeout = -1;
        mySerialPort.WriteTimeout = -1;
        mySerialPort.RtsEnable = false;
        mySerialPort.DtrEnable = true;

        if (!mySerialPort.IsOpen)
        {
            mySerialPort.Open();
        }
        // Place in message mode
        mySerialPort.Write(ctlw, 0, ctlw.Length);
        mySerialPort.Write(MESSAGEMODE);  // Put the PIM in Message Mode.  This variable must NOT be changed
        mySerialPort.Write(cr, 0, cr.Length);

        mySerialPort.Write(ctlt, 0, ctlt.Length);
        mySerialPort.Write(UPBCommandPacket);
        mySerialPort.Write(cr, 0, cr.Length);

        System.Threading.Thread.Sleep(4000);  // Wait four seconds for serial receive communications to complete
        //  Console.WriteLine("Press any key to continue...");
        //   Console.WriteLine();
        //  Console.ReadKey();
        mySerialPort.Close();
    }

    private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort sp = (SerialPort)sender;
        byte[] buffer = new byte[sp.ReadBufferSize];

        // There is no accurate method for checking how many bytes are read
        // unless you check the return from the Read method
        int bytesRead = sp.Read(buffer, 0, buffer.Length);

        // For the example assume the data we are received is ASCII data.
        UPBString += Encoding.ASCII.GetString(buffer, 0, bytesRead);
    }

    public static byte[] ToByteArray(String HexString)
    {
        int NumberChars = HexString.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
        {
            bytes[i / 2] = Convert.ToByte(HexString.Substring(i, 2), 16);
        }
        return bytes;
    }

    static void build_dictionary()
    {
        translate1.Add('0', 0);
        translate1.Add('1', 1);
        translate1.Add('2', 2);
        translate1.Add('3', 3);
        translate1.Add('4', 4);
        translate1.Add('5', 5);
        translate1.Add('6', 6);
        translate1.Add('7', 7);
        translate1.Add('8', 8);
        translate1.Add('9', 9);
        translate1.Add('A', 10);
        translate1.Add('B', 11);
        translate1.Add('C', 12);
        translate1.Add('D', 13);
        translate1.Add('E', 14);
        translate1.Add('F', 15);
        translate1.Add('a', 10);
        translate1.Add('b', 11);
        translate1.Add('c', 12);
        translate1.Add('d', 13);
        translate1.Add('e', 14);
        translate1.Add('f', 15);
    }

    private static string buildPacket(string unit, string state)
    {
        byte csum;
        string messageID;
        string argument1;
        string argument2;

        //            bit 15: LNK: Link Bit -
        //   0 = Direct Packet(DID Field interpreted as a Unit ID).
        //   1 = Link Packet(DID Field interpreted as a Link ID).

        //            bit 14 - REPRQ: Repeater Request - bit 13
        //   00 = Non - Repeater Packet(0 repeats)
        //   01 = Low Repeat Level (1 repeat)
        //   10 = Medium Repeat Level (2 repeats)
        //   11 = High Repeat Level (4 repeats)

        //            bit 12 - LEN: Packet Length - bit 8
        //    The length (in bytes) of the entire UPB Communications Packet (6 - 24 Bytes)

        //            bit 7: RSV: Reserved For Future Use -
        //    Always set to 0.

        //             bit 6 - ACKRQ: Acknowledge Request - bot 4
        //    xx1 = Acknowledge this UPB Packet with an ACK Pulse.
        //    x1x = Acknowledge this UPB Packet with an ID Pulse.
        //    1xx = Acknowledge this UPB Packet with an Acknowledgement Message.

        //                bit 3 - CNT: Transmit Count - bit 2
        //    00 = Transmitting this UPB Packet 1 time.
        //    01 = Transmitting this UPB Packet 2 times.
        //    10 = Transmitting this UPB Packet 3 times.
        //    11 = Transmitting this UPB Packet 4 times.

        //              bit 1 - SEQ: Transmit Sequence - bit 0
        //    00 = Transmitting this UPB Packet for the 1st time.
        //    01 = Transmitting this UPB Packet for the 2nd time.
        //    10 = Transmitting this UPB Packet for the 3rd time.
        //    11 = Transmitting this UPB Packet for the 4th time.

        string packet = "";
        byte[] ControlWordbArray = new byte[2];

        // Using direct packet, non repeater request
        // Control Word as follows:
        //   0000011  1001001 00 01 8D FF            22 64
        //     3       73
        //       037300018DFF
        // 037300018DFF2264

        //  Packet length is 8 bytes

        //  XXXXX is packet length that has to be calculated after the fact
        // R is reserved for future use
        // Acknowledge this UPB Packet with an ID Pulse.
        // Transmitting this UPB Packet 2 times.
        // Transmitting this UPB Packet for the 1st time.
        // 0x22 the Message Data ID is telling the receiving device to goto level 0x64 (100) found
        // in the Message Data Arguments


        // Checksum calculation : Sum all of the bytes of the Packet Header and UPB Message fields together. Then take the 2’s
        // complement of the sum and truncate the result to 8 - bits.
        // 141 on basic
        //0810018DFF2264D5
        // 141 off basic
        //0810018DFF220039       
        if (state == "ON" || state == "OFF")
        {
            messageID = "22"; // Goto  level command
            if (state == "ON")
                argument1 = "64";  // level is 64 hex or 100 decimal
            else
                argument1 = "00";
            argument2 = "FF";  // Execute the GoTo level command at fastest rate of 255 "FF"

            //  There are 9 bytes in this command packet
            //  Control word consists of 2 bytes, Network ID consists of 1 byte,
            //  Destination (target) consists of 1 byte, Source consists of 1 byte
            //  Message ID consistes of 1 byte.  There are two Message agruments,
            //  the first argument 64 is the goto level command and the second
            //  argument is the rate to to to the level.  In this case, FF (255)
            //  use the fastest rate. Finally, the checksum is 1 byte;

            // Set bits in CW to size 9
            ControlWordbArray = setBitIndex(ControlWordbArray, 3);
            ControlWordbArray = setBitIndex(ControlWordbArray, 0);
            ControlWordbArray = setBitIndex(ControlWordbArray, 12);

            packet = String.Format("{0}{1:X}{2:X}", "0", ControlWordbArray[0], ControlWordbArray[1]);
            packet += NetworkID;
            int intValue = Int32.Parse(unit);
            string hexValue = intValue.ToString("X");
            packet += hexValue;
            packet += SourceID;
            packet += messageID;
            packet += argument1;
            packet += argument2;
            csum = checksum(packet);
            string hex = csum.ToString("X2");
            packet = packet + hex;
        }

        return packet;
        // example
        //   ControlWordbArray = setBitIndex(ControlWordbArray, 7);  // Link bit set

        //  ControlWordbArray = setBitIndex(ControlWordbArray, 2);  //  bits 2,1,0
        //  ControlWordbArray = setBitIndex(ControlWordbArray, 1);  //  are packet size
        //   ControlWordbArray = setBitIndex(ControlWordbArray, 0);  // In this example it 4+2 +1 = 7 bytes in size
    }

    private static byte[] setBitIndex(byte[] ba, int index)
    {
        int bitIndex = index;
        int byteIndex = bitIndex / 8;
        int bitInByteIndex = bitIndex % 8;
        byte mask = (byte)(1 << bitInByteIndex);
        bool isSet = (ba[byteIndex] & mask) != 0;
        // set to 1
        ba[byteIndex] |= mask;
        // Set to zero
        //    bArray[byteIndex]  &= (byte) (~mask);
        return ba;
    }
    private static byte checksum(string s)
    {
        string result;
        int temptotal = 0, total = 0;

        for (int x = 0; x < s.Length - 1; x += 2)
        {
            result = s.Substring(x, 2);
            if (translate1.ContainsKey(result[1]))
            {
                temptotal += translate1[result[1]];
            }
            if (translate1.ContainsKey(result[0]))
            {
                temptotal += translate1[result[0]] * 16;
            }
            total += temptotal;
            temptotal = 0;
        }
        total = ~total;
        total += 1;
        byte lower = (byte)(total & 0xff);
        return lower;
    }
}
 
 
 
 
I've been stumped by the following following flow chart for a while.  In one of the decision blocks it states "Get next link ID from Rx Component table". OK, what is a Rx Component table?.  I capture the remote device (switch) returned message packets.  But it's a very basic message packet.  There's just nothing that I can see that can be interpreted as a "Rx component table" within that message packet.
 
So, does anyone know what this "Rx component table" is?
 
 
 
EDIT;
 
This is the packet I'm sending:
0810018DFF2264D5    which means turn on UPB unit 141, In other words, goto 0x64 (full on)
 
The remote device returns the following:
"0810018DFF2264D5PA\rPA\rPK\rPU880C0167FF26647B\rPU880D0167FF26647A\rPU880E0167FF266479\rPU880F0167FF266478\rPU880C01D2FF266410\rPU880D01D2FF26640F\rPU880E01D2FF26640E\rPU880F01D2FF26640D\r\"   
 
 
 
 
 

Attachments

  • UPB.png
    UPB.png
    52.5 KB · Views: 10
The receive component table is going to be the internal table of link ID's stored within the device.  It is also available as an export file from UpStart.
 
This is the reason why HAI developed their HLC method of using UPB.  Since they don't know what devices react to what links, they just poll each device for a given room when a status message appears for any device within that room.
 
If you want to do any accurate status tracking, you'll need to build some data structures that maintain all the link ID's so that you can accurately flag each item when you have a link ID match.
 
Ahaa...  Now I'm starting to understand the UPB protocol.  I downloaded the UPstart Export File version 5.0 from the PCS site and created the Upstart  Export file.  Using both, things are starting to make sense to me.
 
Thanx Jonw.  I really appreciate the info!!!!!
 
Finally finished my .Net project to control UPB devices from a RPI.   I copied the required files over to the RPI and used Mono to execute the binary.  Well, it crashed with an Intermediary Language (IL) error. Eventually, determined that Mono does not yet support the SerialPort class and its associated methods.  Thus, the reason for the crash.  I guess I should have tested the code on the RPI from the very beginning.  It would have saved me a lot of time and effort.
 
So, now I started a Java project to accomplish the same thing on the RPI.  This time I tested the initial code on the RPI AND it DOES work.  I've posted the Java starter code below if anyone is interested in developing a similar project.  You'll need to install the RxTx library on the RPI to execute the code.
 
 
 

import java.util.*;
import java.io.InputStream;
import java.io_OutputStream;
import gnu.io.*;
import java.io.*;
import java.util.Enumeration;
import java.io.IOException;

import java.util.concurrent.LinkedBlockingQueue;

import static java.lang.System.err;
import static java.lang.System.out;

import java.io.IOException;
import java.io.PrintStream;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;


public class UPBRPIClass implements Runnable, SerialPortEventListener {
    static CommPortIdentifier portId;

    static Enumeration portList;
    StringBuffer sb = new StringBuffer();
    InputStream inputStream;
    OutputStream outputStream;

    SerialPort serialPort;

    Thread readThread;

    public static void main(String[] args) {
        portList = CommPortIdentifier.getPortIdentifiers();

        while (portList.hasMoreElements()) {
            portId = (CommPortIdentifier) portList.nextElement();
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                if (portId.getName().equals("/dev/ttyUSB0")) {

                    UPBRPIClass reader = new UPBRPIClass();
                }
            }
        }
    }

    public UPBRPIClass() {
        try {
            serialPort = (SerialPort) portId.open("MainClassApp", 2000);
        } catch (PortInUseException e) {
        }
        try {
            inputStream = serialPort.getInputStream();
        } catch (IOException e) {
        }
        try {
            outputStream = serialPort.getOutputStream();
        } catch (IOException e) {
        }
        try {
            serialPort.addEventListener(this);
        } catch (TooManyListenersException e) {
        }
        serialPort.notifyOnDataAvailable(true);
        try {
            serialPort.setSerialPortParams(4800, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
                                           SerialPort.PARITY_NONE);
        } catch (UnsupportedCommOperationException e) {
        }
        byte[] cr = {0x0D};
        byte[] ctlt =  {0x14} ;
        byte[] ctlw = {0x17};
        String messagemode = "70028E";
        String buffer = "0910018DFF2264FFD5";
        byte[] bytebuffer = buffer.getBytes();
        byte[] msgmode = messagemode.getBytes();
        try {
            outputStream.write(ctlw);
            outputStream.flush();
            outputStream.write(msgmode);
            outputStream.flush();
            outputStream.write(cr);
            outputStream.flush();
            outputStream.write(ctlt);
            outputStream.flush();
            outputStream.write(bytebuffer);
            outputStream.flush();
            outputStream.write(cr);
            outputStream.flush();

        } catch (IOException e) {
            System.err.println("Cannot write:" + e.getMessage());
        }
        readThread = new Thread(this);
        readThread.start();
    }

    public void run() {
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
        }
    }

    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
        case SerialPortEvent.BI:
        case SerialPortEvent.OE:
        case SerialPortEvent.FE:
        case SerialPortEvent.PE:
        case SerialPortEvent.CD:
        case SerialPortEvent.CTS:
        case SerialPortEvent.DSR:
        case SerialPortEvent.RI:
        case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
            break;
        case SerialPortEvent.DATA_AVAILABLE:
            byte[] readBuffer = new byte[20];

            try {
                while (inputStream.available() > 0) {
                    int numBytes = inputStream.read(readBuffer);
                }
                sb.append(new String(readBuffer));
                System.out.print(new String(readBuffer));
                System.out.println(sb.toString());
            } catch (IOException e) {
            }
            break;
        }
    }
}


 
 
 
 
 
Just an update.  I now have Java code which can be used  with a Linux box or Raspberry PI to implement a UPB gateway.  This is a complete Eclipse Java project.  Thus, a solid understanding of the Java programming language is a requirement to implement this project.
 
If anyone is interested in this project including all source code, please PM me. I will give you a link to the project.
 
By purchasing a RPI for $25.00 and using this code, you will have a fully functional UPB gateway as opposed to paying $300.00 for the UPB hardware gateway.
 
 
 
BobS0327,  I would like to get the Eclipse project for this.  Sounds interesting. 
I just got a Rasperry PI 2 and am interested in how I can use this with the OmniPro II.  I also just got an HAI UPB interface and appliance module.  Once I figure out how to get this all working, I am going to use the motion controllers to turn on lights.
 
Well, first of all, the projects that I post are being used to phase out my OPII controller. The OPII controller was designed to implement 20th century home automation. Thus, it no longer fits into my 21st century smart home endeavor. It just doesn't have the performance and capability that I need.
 
It is possible to interface this project to the OPII but it's not something that I personally want to implement. The main driver method of this project has been left purposely simple to allow others to adapt it to their specific needs. In your case, interface it to the OPII. Any future development on my part will not be posted to the forum because it will be highly customized to my specific needs. For example, I will implement this project as a back end server using JSON and RESTful making it accessible from front ends such as mobile devices, web browsers and even Amazon Echo.
 
I built Java project on a Linux Ubuntu 14.04 LTS box using Eclipse Mars 1. You'll need to install the latest version of RXTX on your Linux box and the RPI in order to successfully execute the binary. Also, be sure you export the Upstart file from the latest version of Upstart version 8 Build 41. Ano posted info and a link to this latest version in the HAI subform.
 
Finally, I posted the code in Linux zip format to Filedropper at  http://www.filedropper.com/upbrpi
 
Can you put your projects on github? I would like to be able to mod/contribute if possible and do a proof of concept in my house. Ideally I would love to get these running in docker containers for ultimate flexibility and distribution.
 
rismoney said:
Can you put your projects on github? I would like to be able to mod/contribute if possible and do a proof of concept in my house. Ideally I would love to get these running in docker containers for ultimate flexibility and distribution.
 
Well, this UPB project was just one small  facet of a much larger, more ambitious project.  I'll provide a little background info.  I currently have a Leviton OP2 controller installed which handles all my UPB processing, energy management, security etc.  Unfortunately, the OP2 is an exceptionally SLOW controller relative to the performance/capability requirements of   a "smart home"  project that I want to implement.
 
I plan to use a Beowolf RPI computer cluster configuration to handle the "intelligence" requirements of "smart home" implementation.  The Beowolf project will use Message Passing Interface (MPI) in a parallel processing environment.  Thus,using the OP2 in this environment would be like driving a Model T down the technology highway instead of using a Ferrari.  So, I have to dismantle the OP2 functionality and build it into Beowolf.
 
But anyway, getting back to your original question.  The UPB gateway code I'm working on isn't really stable yet. But as soon as I feel comfortable with it, I will create a github repo.  Possibly in a week or two.  I'll post on the forum when the repo is up.
 
rismoney said:
Can you put your projects on github? I would like to be able to mod/contribute if possible and do a proof of concept in my house. Ideally I would love to get these running in docker containers for ultimate flexibility and distribution.
I just put a repo on github.  The link to the code is listed below.  You'll probably have to change the  COM port and recompile the code. 
https://github.com/search?utf8=%E2%9C%93&q=upbserver
 
I plan to turn it into a async back end server which will allow mobile devices, tablets etc to remotely control UPB devices and also get the status of UPB devices.  IOW, a UPB gateway.  I will update the repo as I progress.  But keep in mind that I am a software development Team of ONE.  So, progress may be slow at times.
 
Just pushed another update to github   https://github.com/search?utf8=%E2%9C%93&q=upbserver
 
This update does the following:
 
Parses the UPstart export file into a SQLite database file
Now uses a configuration file to load variables such as comm port, file locations etc on application startup
Stores the status on/off etc of UPB devices in the SQLite database.
 
Next update will have a front end client which will allow you to control your UPB devices via mobile, tablets etc. from anywhere in the world.
 
Please note that this is ALPHA code.  As I said before, I'm a ONE person software development team.
 
Just pushed another update to github. 
 
I have added remote access to this update.  That is, you can control (and get the status) of your UPB switches from anywhere in the world by simply issuing commands to the back end Java server using any web browser.  The back end server returns a JSON string if the command is successful.
 
I will probably add security features to the app on the next update. IOW,  a userid and password will be needed to access the server.  This data will be encrypted using AES algorithms prior to being transmitted across the net.
 
Finally, read the Readme file for details on the app.
 
Just pushed another update to github.
 
Still an ALPHA app but I continue testing it.  It's fairly stable.  You can now access the UPB server remotely using the ActivateLink, DeactivateLink, Goto, Blink and Status UPB commands. It now accurately maintains a database of the most current status of all UPB devices.
 
One or two more updates will make it a finished product for me.  I intend to add  AES encryption for remote access, server diagnostics to monitor health of UPB server and take corrective action such as rebooting server if network/serial connectivity is lost.  And to wrap it up, I'll add a Android app for remote connectivity.  After which, I'm calling it a day and moving onto another project.
 
 
 
 
Just added another repo to Github.  It is a  Java GUI front end called upbClient to access the back end upbServer.  Git hub link is https://github.com/search?utf8=%E2%9C%93&q=upbclient
 
Also extensively updated the upbServer Github repo.  Updates include receiving emails every 24 hours reporting on the health (status) of the server since it is designed to be run as a headless back end server.  Github link is https://github.com/BobS0327/upbserver
 
The upbClient is considered to be alpha work since I really don't need this type of GUI front end.  It was created to demonstrate this client/server app.  My specific needs dictate that I will need to use Pubnub  https://www.pubnub.com/ for my smart home project.
 
 
 
 
Back
Top