Wednesday, January 29, 2014

Automated Time Sync via Serial Communication with Valcom V-2924A

We had a problem with our Valcom V-2924A losing time.  It would drift up to a couple of minutes per month.  The only way to correct the time is to hook up a laptop up to the serial port and run the Valcom program, and manually adjust the time.  This would work fine, except the clock has such a huge drift.  We even replaced the Valcom unit, and the replacement wasn't any better.  The clock drift itself wouldn't even be that big of a problem, except this unit runs our buzzers at the beginning and end of the shift.  It has no way of synchronizing with our time clocks.

I downloaded a program called Free Serial Port Monitor at http://www.serial-port-monitor.com/.  I told it to monitor my com port, and then I ran the Valcom program to check the time and write the time back.  Once I found the commands it was using to set the time, I replicated those commands in a c# console application. The only problem I found was at the end of the time string going to the Valcom, it had a hexadecimal number between (decimal) 672 and 720.  I couldn't figure out how it knows which value to pick, but it obviously needs a correct value here to work.  Since I couldn't ever figure out which one it needed, I just looped and sent them all.  Since that only worked most of the time, I sent them each 10 times.  I'm assuming those three digits are a checksum of some kind, but regardless the brute force method seemed to work well.

Since we have a dedicated computer hooked up to the Valcom to set the time once a month anyway, I scheduled a task on it to run my C# console application every hour.  No more drifting time.

Here's my code:


static class Program {
       static SerialPort serialPort = new SerialPort();

        static void Main(string[] args) {
           serialPort.PortName = LoadComPortFromCSV();
           serialPort.BaudRate = 9600;
           serialPort.Parity = Parity.None;
           serialPort.DataBits = 8;
           serialPort.StopBits = StopBits.One;
           //serialPort.Handshake = Handshake.XOnXOff;
           serialPort.DataReceived += serialPort_DataReceived;
           serialPort.DtrEnable = true;
           serialPort.RtsEnable = true;
           OpenSerial();

           if (serialPort.IsOpen) {
               serialPort.WriteLine("U010B6");
               serialPort.Write(new byte[] { 13, 10 }, 0, 2);
               Console.WriteLine("Sending U010B6");
               System.Threading.Thread.Sleep(3000);

               
               for (int j = 0; j < 5; j++) {
                   for (var i = 672; i < 720; i++) {
                       serialPort.Write("T" + DateTime.Now.ToString("HHmmssMMddyy") + i.ToString("X"));
                       serialPort.Write(new byte[] {13, 10}, 0, 2);
                       Console.WriteLine("Sending T" + DateTime.Now.ToString("HHmmssMMddyy") + i.ToString("X"));
                       System.Threading.Thread.Sleep(50);
                   }
               }

               Console.WriteLine();
           }
        }

        private static String LoadComPortFromCSV() {
            var port = string.Empty;
            try {
                var reader = new StreamReader(File.OpenRead(Environment.CurrentDirectory +  @"\port.csv"));
                while (!reader.EndOfStream) {
                    port = reader.ReadLine().TrimStart();
                }
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message);
            }
            return port;
        }

        static void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) {
            var sp = (SerialPort)sender;
            var indata = sp.ReadExisting();
            Console.Write(indata);
            CloseSerial();
            }

        public static void OpenSerial() {
            try {
                if (!serialPort.IsOpen) {
                    Console.WriteLine("Opening " + serialPort.PortName);
                    serialPort.Open();
                }}
            catch (Exception ex) {
                Console.WriteLine(ex.Message);
            }
        }

        public static void CloseSerial() {
            try { if (!serialPort.IsOpen) serialPort.Close();}
            catch (Exception ex) {
                Console.WriteLine(ex.Message);
            }
        }
    }

No comments:

Post a Comment