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;
}
}
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;
}
}