//----------------------------------------------------------------------- // // Copyright © 2012 Nils Hammar. All rights reserved. // //----------------------------------------------------------------------- /* * Software to access vehicle information via the OBD-II connector. * * Copyright © 2012 Nils Hammar * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Alternative licensing is possible, see the licensing document. * * The above text may not be removed or modified. */ namespace Protocol { using System; using System.Collections.Generic; using System.ComponentModel; using System.IO.Ports; using System.Linq; using System.Threading; using System.Windows.Forms; using global::FtdiApi; using global::FtdiApi.Constants; using global::FtdiApi.Exceptions; using global::SharedObjects; /// /// Class for communication over a simple serial device like the VAG-Comm or Tactrix 1.3 cable. /// public sealed class SimpleSerialDevice : IDisposable { /// /// Start communication request. /// /// For ISO14230 fast init. /// /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Reviewed.")] private static readonly byte[] START_COMM_REQ = new byte[] { 0xc1, 0x33, 0xf1, 0x81, 0x66 }; /// /// Keepalive request. /// /// For ISO 9141. /// /// private static readonly byte[] KEEPALIVE_REQ_1 = new byte[] { 0x68, 0x6a, 0xf1, 0x01, 0x00, 0xc4 }; /// /// Keepalive request. /// /// For ISO ISO14230 5 baud init. /// /// private static readonly byte[] KEEPALIVE_REQ_2 = new byte[] { 0xc1, 0x33, 0xf1, 0x3e, 0x23 }; //// private static readonly byte[] KEEPALIVE_REQ_2 = new byte[] { 0xc2, 0x33, 0xf1, 0x01, 0x00, 0xe7 }; /// /// Byte pairs that are answers on five baud init request. /// private static readonly byte[][] EXPECT_BYTE_PAIRS = new byte[][] { new byte[] { 0x08, 0x08 }, new byte[] { 0x94, 0x94 }, new byte[] { 0xe9, 0x8f }, new byte[] { 0x6b, 0x8f }, new byte[] { 0x6d, 0x8f }, new byte[] { 0xef, 0x8f }, }; /// /// Bytes to expect the second time around. /// private static readonly byte[][] EXPECT_BYTE2 = new byte[][] { new byte[] { 0xcc }, }; /// /// Current FTDI API instance. /// private FTDI ftdi; /// /// Current FTDI port. /// private FtdiPort ftdiPort = null; /// /// Current serial port using Windows API. /// private SerialPort serialPort1 = null; /// /// Lock object to avoid race condition on writing data to port. /// private object lockObject2 = new object(); /// /// Current serial port using FTDI API. /// private string comPort = null; /// /// Default communication speed. /// private int speed = 10400; /// /// Current expect byte set. /// private byte[][] expectByteSet = null; /// /// Bytes received. /// private byte[] rxBytes = null; /// /// Initialization thread for the communication. /// private Thread initThread; /// /// Serial data event handler. /// private SerialDataReceivedEventHandler serialDataReceivedEventHandler = null; /// /// Timestamp for last read of data. /// private long lastReadTs = 0; /// /// Index of received data. /// private int rxIx = 0; /// /// Length of expected bytes. /// private int expectByteLength = 0; /// /// Delay for five baud init. /// private int newDelay = 200; /// /// Text logging instance. /// private ILogging iTextLogging; /// /// Object for synchronization between threads. /// private object lockObject = new object(); /// /// Wait index value, indicates how the connection handshake progress is. /// private int wIndex = 0; /// /// Delta time between incoming characters. /// private long delta = 0; /// /// Keepalive thread control flag. /// private bool doRun = true; /// /// Characters to discard. /// private int discardChars = 0; /// /// Event handling for FTDI API. /// private AutoResetEvent receivedDataEvent; /// /// Data receiver worker instance. /// private BackgroundWorker dataReceivedHandler; /// /// Flag indicating that the FTDI api is valid. /// private bool ftdiValidData = true; /// /// Flag indicating that a Break was received. /// private bool breakFlag = false; /// /// Request to send flag. /// private bool rtsFlag = false; /// /// Data Terminal Ready flag. /// private bool dtrFlag = false; /// /// Expectation stage ID. /// private int expectStage = 0; /// /// Previous modem status, used to detect changes of signals. /// private byte oldModemStatus = 0; /// /// Flag indicating if connection is established or not. /// private bool connected = false; /// /// Gets or sets a value indicating whether the L-Line shall be used. /// public bool lLine { get; set; } /// /// Gets or sets a value indicating whether calibration shall be done. /// public bool calibrate { get; set; } /// /// Gets a value indicating whether the Carrier Detect state is active. /// public bool cdState { get { if (this.ftdiPort != null) { byte mdmStat = 0; this.ftdiPort.GetModemStatus(ref mdmStat); return ((mdmStat & FT_MODEM_STATUS.FT_DCD) != 0); } return this.serialPort1.CDHolding; } } /// /// Gets a value indicating whether the Clear to Send state is active. /// public bool ctsState { get { if (this.ftdiPort != null) { byte mdmStat = 0; this.ftdiPort.GetModemStatus(ref mdmStat); return ((mdmStat & FT_MODEM_STATUS.FT_CTS) != 0); } return this.serialPort1.CtsHolding; } } /// /// Gets a value indicating whether the Data Set Ready state is active. /// public bool dsrState { get { if (this.ftdiPort != null) { byte mdmStat = 0; this.ftdiPort.GetModemStatus(ref mdmStat); return ((mdmStat & FT_MODEM_STATUS.FT_DSR) != 0); } return this.serialPort1.DsrHolding; } } /// /// Gets or sets a value indicating whether Request to Send state shall be active. /// public bool rtsState { get { if (this.ftdiPort != null) { return this.rtsFlag; } return this.serialPort1.RtsEnable; } set { this.rtsFlag = value; if (this.ftdiPort != null) { this.ftdiPort.SetRTS(this.rtsFlag); } else { this.serialPort1.RtsEnable = this.rtsFlag; } } } /// /// Gets or sets a value indicating whether Data Terminal Ready state shall be active. /// public bool dtrState { get { if (this.ftdiPort != null) { return this.dtrFlag; } return this.serialPort1.DtrEnable; } set { this.dtrFlag = value; if (this.ftdiPort != null) { this.ftdiPort.SetDTR(this.dtrFlag); } else { this.serialPort1.DtrEnable = this.dtrFlag; } } } /// /// Gets or sets a value indicating whether Break state shall be active. /// public bool breakState { get { if (this.ftdiPort != null) { return this.breakFlag; } return this.serialPort1.BreakState; } set { this.breakFlag = value; if (this.ftdiPort != null) { this.ftdiPort.SetBreak(this.breakFlag); } else { this.serialPort1.BreakState = this.breakFlag; } } } /// /// Gets the detected W1 parameter. /// public int w1 { get; private set; } /// /// Gets the detected W2 parameter. /// public int w2 { get; private set; } /// /// Gets the detected W3 parameter. /// public int w3 { get; private set; } /// /// Gets the W4 parameter. /// public int w4 { get; private set; } /// /// Gets or sets baud rate for connection. /// public int baudRate { get { return this.speed; } set { this.speed = value; if (this.ftdiPort != null) { this.ftdiPort.SetBaudRate((uint)this.speed); } else { if (this.serialPort1 != null) { this.serialPort1.BaudRate = this.speed; } } } } /// /// Gets or sets a value indicating whether the line status data changes shall be echoed. /// public bool echoLine { get; set; } /// /// Gets or sets a value indicating whether modem signal changes shall be echoed. /// public bool echoModem { get; set; } /// /// Selected FTDI port info data. /// private FT_DEVICE_INFO_NODE selectedFtdiPort; /// /// Initializes a new instance of the class. /// /// FTDI access instance. /// Com port name, /// Selected FTDI port. /// Communication speed. /// Text logging interface instance. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "comPort", Justification = "Reviewed.")] public SimpleSerialDevice(FTDI ftdi, string comPort, FT_DEVICE_INFO_NODE selectedFtdiPort, int speed, ILogging iTextLogging) { this.ftdi = ftdi; this.comPort = comPort; this.selectedFtdiPort = selectedFtdiPort; this.speed = speed; this.iTextLogging = iTextLogging; this.echoLine = false; this.echoModem = false; this.calibrate = true; this.lLine = false; this.w1 = 0; this.w2 = 0; this.w3 = 0; this.w4 = 25; } /// /// Get array of FTDI devices. /// /// Array of FTDI devices. public static FT_DEVICE_INFO_NODE[] ftdiDevices() { FT_DEVICE_INFO_NODE[] newDeviceNames = null; List deviceNameList = new List(); FTDI ftdi = new FTDI(); try { uint devcount = 0; ftdi.GetNumberOfDevices(ref devcount); FT_DEVICE_INFO_NODE[] devicelist = new FT_DEVICE_INFO_NODE[devcount]; ftdi.GetDeviceList(devicelist); foreach (FT_DEVICE_INFO_NODE deviceItem in devicelist) { if (deviceItem != null && !string.IsNullOrEmpty(deviceItem.Description)) { deviceNameList.Add(deviceItem); } } } catch (FT_EXCEPTION ex) { MessageBox.Show(ex.GetType().ToString() + "\r\n" + ex.Message); } finally { newDeviceNames = deviceNameList.ToArray(); } return newDeviceNames; } /// /// Initialize the communication using a background thread. /// public void init() { this.initThread = new Thread(this.initThreadImpl); this.initThread.Start(); } /// /// Clean up resources. /// public void Dispose() { this.close(); this.dataReceivedHandler.Dispose(); this.receivedDataEvent.Dispose(); } /// /// Open a new connection. /// public void open() { // if (this.comPort.StartsWith("FTDI:")) if (this.selectedFtdiPort != null) { this.ftdiPort = new FtdiPort(this.ftdi); this.ftdiValidData = true; this.ftdiPort.OpenByLocation(this.selectedFtdiPort.LocId); this.ftdiPort.SetBaudRate((uint)this.speed); this.ftdiPort.SetDataCharacteristics( FT_DATA_BITS.FT_BITS_8, FT_STOP_BITS.FT_STOP_BITS_1, FT_PARITY.FT_PARITY_NONE); this.ftdiPort.SetLatency(2); this.ftdiPort.SetFlowControl(FT_FLOW_CONTROL.FT_FLOW_NONE, 0, 0); this.receivedDataEvent = new AutoResetEvent(false); this.ftdiPort.SetEventNotification( FT_EVENTS.FT_EVENT_RXCHAR | FT_EVENTS.FT_EVENT_LINE_STATUS | FT_EVENTS.FT_EVENT_MODEM_STATUS, this.receivedDataEvent); this.dataReceivedHandler = new BackgroundWorker(); this.dataReceivedHandler.DoWork += this.ReadData; if (!this.dataReceivedHandler.IsBusy) { this.dataReceivedHandler.RunWorkerAsync(); } } else { this.serialPort1 = new SerialPort(this.comPort, this.speed, Parity.None, 8, StopBits.One); this.serialPort1.Handshake = Handshake.None; this.serialPort1.Open(); this.serialPort1.ErrorReceived += new System.IO.Ports.SerialErrorReceivedEventHandler(this.serialPort1_ErrorReceived); this.serialPort1.PinChanged += new System.IO.Ports.SerialPinChangedEventHandler(this.serialPort1_PinChanged); this.serialDataReceivedEventHandler = new System.IO.Ports.SerialDataReceivedEventHandler(this.serialPort1_DataReceived); this.serialPort1.DataReceived += this.serialDataReceivedEventHandler; this.iTextLogging.appendText("Encoding=" + this.serialPort1.Encoding.EncodingName + "\r\n"); } } /// /// Close the current connection. /// public void close() { this.doRun = false; this.connected = false; if (this.initThread != null) { this.initThread.Abort(); this.initThread.Interrupt(); this.initThread.Join(4000); } if (this.ftdiPort != null) { try { this.ftdiPort.Close(); } catch (Exception ex) { this.iTextLogging.appendText("ftdi; Close failed: " + ex.Message + "\r\n"); } finally { this.ftdiPort = null; } } if (this.serialPort1 != null) { try { this.serialPort1.Close(); } catch (Exception ex) { this.iTextLogging.appendText("serialPort1; Close failed: " + ex.Message + "\r\n"); } finally { this.serialPort1 = null; } } } /// /// Write data to device. /// /// Data to write. public void write(byte[] ba0) { if (ba0 != null) { lock (this.lockObject2) { foreach (byte b0 in ba0) { byte[] ba = new byte[] { b0 }; if (this.ftdiPort != null) { uint written = 0; this.ftdiPort.Write(ba, ba.Length, ref written); if (written != ba.Length) { this.iTextLogging.appendText("\r\nWritten less than provided! (written=" + written + ", requested=" + ba.Length + ")\r\n"); } } else { this.serialPort1.Write(ba, 0, ba.Length); } Thread.Sleep(10); } } } } /// /// Build initialization packet. /// /// Initialization packet data. private static uint buildInitPacket() { byte startByte = 0x0; byte stopByte = 0xff; byte contentByte = 0x33; uint dataPacket = (uint)(stopByte & 0x01); dataPacket = (uint)(dataPacket << 8) | (uint)(contentByte & 0xff); dataPacket = (uint)(dataPacket << 1) | (uint)(startByte & 0x01); return dataPacket; } /// /// Perform port event tasks. /// private void portEvent() { if (this.ftdiPort != null) { this.handleModemStatus(); this.handleLineStatus(); this.readData(); } } /// /// Read data from device. /// /// Sending item. /// Event arguments. private void ReadData(object pSender, DoWorkEventArgs pEventArgs) { try { while (true) { // wait until event is fired this.receivedDataEvent.WaitOne(); this.portEvent(); } } catch (Exception ex) { this.iTextLogging.appendText("ReadData failed: " + ex.Message + "\r\n" + ex.StackTrace + "\r\n"); } } /// /// Read data. /// /// Data read. private uint readData() { uint nrOfBytesAvailable = 0; // try to recieve data now this.ftdiPort.GetRxBytesAvailable(ref nrOfBytesAvailable); // If data exists and is valid. if (this.ftdiValidData && nrOfBytesAvailable > 0) { byte[] readData = new byte[nrOfBytesAvailable]; uint numBytesRead = 0; this.ftdiPort.Read(readData, nrOfBytesAvailable, ref numBytesRead); long t1 = DateTime.Now.Ticks; this.delta = (t1 - this.lastReadTs) / (TimeSpan.TicksPerSecond / 1000); if (this.delta > 200) { this.iTextLogging.appendText("\r\n"); } this.iTextLogging.appendText(" [" + this.delta + "]"); this.lastReadTs = t1; this.iTextLogging.appendText(" {" + numBytesRead + "}"); for (int i = 0; i < numBytesRead && i < nrOfBytesAvailable; i++) { this.handleChar(readData[i]); } } return nrOfBytesAvailable; } /// /// Handle changes in modem status lines. /// private void handleModemStatus() { if (this.echoLine) { byte modemStatus = 0; this.ftdiPort.GetModemStatus(ref modemStatus); if (modemStatus != this.oldModemStatus) { if ((FT_MODEM_STATUS.FT_CTS & modemStatus) != (FT_MODEM_STATUS.FT_CTS & this.oldModemStatus)) { if ((FT_MODEM_STATUS.FT_CTS & modemStatus) != 0) { this.iTextLogging.appendText("◅CTS+▻"); } else { this.iTextLogging.appendText("◅CTS-▻"); } } if ((FT_MODEM_STATUS.FT_DCD & modemStatus) != (FT_MODEM_STATUS.FT_DCD & this.oldModemStatus)) { if ((FT_MODEM_STATUS.FT_DCD & modemStatus) != 0) { this.iTextLogging.appendText("◅DCD+▻"); } else { this.iTextLogging.appendText("◅DCD-▻"); } } if ((FT_MODEM_STATUS.FT_DSR & modemStatus) != (FT_MODEM_STATUS.FT_DSR & this.oldModemStatus)) { if ((FT_MODEM_STATUS.FT_DSR & modemStatus) != 0) { this.iTextLogging.appendText("◅DSR+▻"); } else { this.iTextLogging.appendText("◅DSR-▻"); } } if ((FT_MODEM_STATUS.FT_RI & modemStatus) != (FT_MODEM_STATUS.FT_RI & this.oldModemStatus)) { if ((FT_MODEM_STATUS.FT_RI & modemStatus) != 0) { this.iTextLogging.appendText("◅RI+▻"); } else { this.iTextLogging.appendText("◅RI-▻"); } } this.oldModemStatus = modemStatus; } } } /// /// Handle data line information/errors. /// private void handleLineStatus() { if (this.echoModem) { byte lineStatus = 0; this.ftdiPort.GetLineStatus(ref lineStatus); if (lineStatus > 0) { if ((FT_LINE_STATUS.FT_BI & lineStatus) != 0) { this.iTextLogging.appendText("⌦BREAK⌫"); } if ((FT_LINE_STATUS.FT_FE & lineStatus) != 0) { this.iTextLogging.appendText("⌦Framing Error⌫"); } if ((FT_LINE_STATUS.FT_OE & lineStatus) != 0) { this.iTextLogging.appendText("⌦Overrun Error⌫"); } if ((FT_LINE_STATUS.FT_PE & lineStatus) != 0) { this.iTextLogging.appendText("⌦Parity Error⌫"); } } } } /// /// Read characters. /// private void readChars() { while (this.serialPort1.BytesToRead > 0) { this.readChar(); } } /// /// Read single character. /// private void readChar() { int ch = this.serialPort1.ReadChar(); this.handleChar(ch); } /// /// Handle single character. /// /// Character to handle. private void handleChar(int ch) { if (this.connected) { if (this.discardChars > 0) { this.discardChars--; } else { this.iTextLogging.appendText(" " + ch.ToString("x2")); } } else { if (ch == 0x55 && this.wIndex == 0) { this.w1 = (int)this.delta; this.wIndex++; this.iTextLogging.appendText("!"); } this.iTextLogging.appendText(" " + ch.ToString("x2")); if (this.expectByteSet != null) { if (this.rxBytes == null) { foreach (byte[] expectBytes1 in this.expectByteSet) { this.expectByteLength = (this.expectByteLength < expectBytes1.Length) ? expectBytes1.Length : this.expectByteLength; } this.rxBytes = new byte[this.expectByteLength]; this.rxIx = 0; } bool match = false; foreach (byte[] expectBytes1 in this.expectByteSet) { if (this.rxIx < expectBytes1.Length && expectBytes1[this.rxIx] == (byte)ch) { match = true; this.rxBytes[this.rxIx] = (byte)ch; break; } } if (match && this.expectStage == 1) { if (this.wIndex == 1 && this.rxIx == 0) { this.w2 = (int)this.delta; } if (this.wIndex == 1 && this.rxIx == 1) { this.w3 = (int)this.delta; this.wIndex += 2; Thread.Sleep(this.w4); this.ackKeyBytes(this.rxBytes[this.rxIx]); this.expectStage = 2; } this.rxIx++; } else { if (match && this.expectStage == 2) { this.rxIx++; } else { this.rxIx = 0; } } if (this.rxIx == this.expectByteLength) { lock (this.lockObject) { Monitor.PulseAll(this.lockObject); } } } } } /// /// Initialization thread implementation. /// private void initThreadImpl() { this.doRun = true; try { this.iTextLogging.appendText("5 baud init.\r\n"); this.calibrateDelay(); uint dataPacket = buildInitPacket(); this.rxBytes = null; this.rxIx = 0; this.expectByteSet = EXPECT_BYTE_PAIRS; this.expectByteLength = 0; this.expectStage = 1; this.wIndex = 0; this.sendInitPacket(dataPacket); this.iTextLogging.appendText("\r\nExpect data...\r\n"); lock (this.lockObject) { Monitor.Wait(this.lockObject, 4000); } if (this.rxBytes == null) { this.iTextLogging.appendText("\r\nNo data 1...\r\n"); return; } int keyWordIndex; bool match; byte keyByte2; this.verifyResponse(out keyWordIndex, out match, out keyByte2); // Clear and set next expected data. this.rxBytes = null; this.rxIx = 0; this.expectByteSet = EXPECT_BYTE2; this.expectByteLength = 0; if (!match) { this.iTextLogging.appendText("\r\nNo Match 1! - Exiting!\r\n"); return; } // Write log event here to minimize timing error impact. this.iTextLogging.appendText("\r\nMatch 1!\r\n"); // Wait for second expected data (0xcc). lock (this.lockObject) { Monitor.Wait(this.lockObject, 4000); } if (this.rxBytes == null) { this.iTextLogging.appendText("\r\nNo data 2...\r\n"); return; } int ix = 0; this.verifyResponse(out ix, out match, out keyByte2); if (!match) { this.iTextLogging.appendText("\r\nNo Match 2! - Exiting!\r\n"); return; } this.iTextLogging.appendText("\r\nMatch 2! keyWordIndex=" + keyWordIndex + "\r\n"); this.displayInfo(keyWordIndex); byte[] keepAliveData; if (keyWordIndex <= 1) { keepAliveData = KEEPALIVE_REQ_1; } else { keepAliveData = KEEPALIVE_REQ_2; } this.connected = true; Thread.Sleep(100); while (this.doRun) { this.discardChars = keepAliveData.Length; this.write(keepAliveData); Thread.Sleep(3000); } } catch (InvalidOperationException) { // Ignore. } } /// /// Display protocol information. /// /// Protocol index value. private void displayInfo(int ix) { switch (ix) { case 0: this.iTextLogging.appendText("Protocol ISO9141 [0x0808]\r\n"); break; case 1: this.iTextLogging.appendText("Protocol ISO9141 [0x9494]\r\n"); break; case 2: this.iTextLogging.appendText("Protocol ISO14230 [0x8fe9]\r\n"); break; case 3: this.iTextLogging.appendText("Protocol ISO14230 [0x8f6b]\r\n"); break; case 4: this.iTextLogging.appendText("Protocol ISO14230 [0x8f6d]\r\n"); break; case 5: this.iTextLogging.appendText("Protocol ISO14230 [0x8fef]\r\n"); break; } this.iTextLogging.appendText("W1=" + this.w1 + ", W2=" + this.w2 + ", W3=" + this.w3 + ", W4=" + this.w4 + "\r\n"); } /// /// Acknowledge key byte. /// /// Key byte to acknowledge. private void ackKeyBytes(byte keyByte2) { byte[] buf = new byte[1]; buf[0] = (byte)(~keyByte2 & 0xff); this.iTextLogging.appendText(""); this.write(buf); } /// /// Verify response data against expected data. /// /// Index value for matching data. /// Flag indicating if there was a match. /// Key byte value. private void verifyResponse(out int ix, out bool match, out byte keyByte2) { ix = 0; keyByte2 = 0x00; match = false; foreach (byte[] expectBytes1 in this.expectByteSet) { string s2 = string.Empty; for (int i = 0; i < expectBytes1.Length; i++) { s2 += ", 0x" + expectBytes1[i].ToString("x2"); } match = true; for (int i = 0; i < this.rxBytes.Length && i < expectBytes1.Length; i++) { if (expectBytes1[i] != this.rxBytes[i]) { match = false; break; } } if (match) { if (this.rxBytes.Length >= 2) { keyByte2 = this.rxBytes[1]; } break; } ix++; } this.iTextLogging.appendText("ix=" + ix + "\r\n"); } /// /// Send initialization packet. /// /// Data packet to send. private void sendInitPacket(uint dataPacket) { if (this.ftdiPort != null) { this.ftdiValidData = false; } else { this.serialPort1.DataReceived -= this.serialDataReceivedEventHandler; } if (this.ftdiPort != null) { this.ftdiPort.Purge(FT_PURGE.FT_PURGE_RX); } else { this.serialPort1.DiscardInBuffer(); } this.txData(10, this.newDelay, dataPacket); // Just in case. this.breakState = false; // To get correct time for W1 this.lastReadTs = DateTime.Now.Ticks; if (this.ftdiPort != null) { this.ftdiValidData = true; } else { this.serialPort1.DataReceived += this.serialDataReceivedEventHandler; } } /// /// Calibrate delay value to compensate for delays in the computer. /// private void calibrateDelay() { if (this.calibrate) { this.iTextLogging.appendText("Calibrating for 5 baud tx.\r\n"); this.newDelay = this.calibrateFiveBaud(); if (this.newDelay < 0) { this.iTextLogging.appendText("Error! Negative delay calculated!\r\n"); throw new Exception("Negative delay calculated!"); } } this.iTextLogging.appendText("\r\nnewDelay=" + this.newDelay + "\r\n"); } /// /// Calibrate the delay for correct transmission of five baud data. /// /// Calibrated delay value. private int calibrateFiveBaud() { int bits = 32; int delay = 200; long t0 = 0; long t1 = 0; this.txData(bits, delay, 0xff0000ff, out t0, out t1); long ticksPerBit = (t1 - t0) / bits; long timePerBit = (ticksPerBit / 10000); int newDelay1 = (int)(delay - (timePerBit - delay)); this.iTextLogging.appendText("Calibrated!\r\n"); // Just in case. this.breakState = false; Thread.Sleep(300); return newDelay1; } /// /// Send 5 baud data. /// /// Bits to send. /// Delay value. /// Data packet to send. private void txData(int bits, int delay, uint dataPacket) { long t0; long t1; this.txData(bits, delay, dataPacket, out t0, out t1); } /// /// Send 5 baud data. /// /// Bits to send. /// Delay value. /// Data packet to send. /// Start time. /// End time. private void txData(int bits, int delay, uint dataPacket, out long t0, out long t1) { ThreadPriority currentPrio = Thread.CurrentThread.Priority; try { Thread.CurrentThread.Priority = ThreadPriority.Highest; t0 = DateTime.Now.Ticks; for (int i = 0; i < bits; i++) { if ((dataPacket & (1 << i)) != 0) { this.setLineState(false); this.iTextLogging.appendText("1"); } else { this.setLineState(true); this.iTextLogging.appendText("0"); } Thread.Sleep(delay); } t1 = DateTime.Now.Ticks; } finally { Thread.CurrentThread.Priority = currentPrio; } } /// /// Set signal line status value. /// /// State for signal. private void setLineState(bool state) { if (this.lLine) { this.dtrState = state; } this.breakState = state; } /// /// Handle error event from serial port. /// /// Sending object. /// Event Data. private void serialPort1_ErrorReceived(object sender, SerialErrorReceivedEventArgs e) { if (this.serialPort1 != null) { SerialError err = e.EventType; this.iTextLogging.appendText("Error Received :" + this.serialPort1.PortName + ", error=" + err + "\r\n"); } } /// /// Handle Data Received event. /// /// Sending object. /// Event Data. private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { if (this.serialPort1 != null) { SerialData serialData = e.EventType; this.readChars(); } } /// /// Handle pin changed event. /// /// Sending object. /// Event Data. private void serialPort1_PinChanged(object sender, SerialPinChangedEventArgs e) { /* if (this.serialPort1 != null) { SerialPinChange spc = e.EventType; } */ } } }