4

I have a Coin Counter Machine(SC350/360) which connects to a computer via RS232C Interface. I've the technical documentation which describes the communication protocols and a working pascal program is also included for manipulating the machine. I copied the pascal code and tested it on Turbo Pascal, using DosBox, for windows 7 64 bit and the code compiles successfully. What I wanted to achieve now was to convert those pascal codes to C#.Net but I was having a hard time when converting few lines to C# as I don't have much experience on serial port programming.

This was the code in Pascal to Initialize communication with the machine. (Set baudrate to 9600, 8 bits, no parity, 1 stop bit)

uses crt;
const
{ COM1: RS232 port address }
RXTX = $3F8; { $2F8 if COM2: is used }
ACK = 6;
NAK = 21;
ESC = 27;
var
dummy,
checkSum : integer;
key : char;
protocol : integer;

    var i : integer;
begin
i := 1843200 div 9600 div 16;
port[RXTX + 3] := $80;
port[RXTX + 1] := hi(i);
port[RXTX]:= lo(i);
port[RXTX + 3] := 3;
port[RXTX + 4] := $A;
while odd(port[RXTX + 5]) do
begin
dummy := port[RXTX];
delay(10);
end;
end; { InitComm }

The corresponding C# for the above code I came up was; (Please correct me if I get it wrong)

SerialPort port=new SerialPort("COM1",9600,Parity.None,8,StopBits.One);

But I couldn't understand how to convert the rest of the pascal procedures. Some of these procedures i had difficulty with are;

procedure Tx(data : integer);
{ Transmit a character on serial channel }
begin
while port[RXTX + 5] and $20 = 0 do;
port[RXTX] := data and $FF;
end; { Tx }

function RxWait : integer;
{ Waits for a character from serial channel }
begin
while not odd(port[RXTX + 5]) do;
RxWait := port[RXTX];
end; { RxWait }

procedure Tx2(data : integer);
{ Transmit a char on serial channel + Calculate check sum }
begin
Tx(data);
checkSum := (checkSum + data) and $FF;
end; { Tx2 }

Could you guys please help me how to convert those pascal codes to the equivalent C#? I know I can write to a port using the 'port.Write' Method but that couldn't exactly fit the turbo pascal code with the port array.(e.g port[RXTX + 3] := $80;) I don't know what the port array index 'RXTX+3' is referring relating to C#.

I would really appreciate it if you could give me a hand on this and I hope I'll learn to convert the rest of the pascal codes myself. :)

I have written the following equivalent C# code for the pascal program using the help I got from the good people here. Please correct me if i have made a mistake in my code.

            public void Tx(int data)
            {
                if (!port.IsOpen)
                    port.Open();
                port.Write(new byte[] { (byte)(data & 0xFF) }, 0, 1);
                port.Close();
            }
            /// <summary>
            /// Wait for a character from serial channel
            /// </summary>
            /// <returns></returns>
            public int RxWait()
            {
                if (!port.IsOpen)
                    port.Open();
                int readByte = port.ReadByte();
                port.Close();
                return readByte;
            }
            /// <summary>
            /// Transmit a char on serial channel + Calculate check sum
            /// </summary>
            /// <param name="data"></param>
            public void Tx2(int data)
            {
                Tx(data);
                checkSum = (checkSum + data) & 0xFF;
            }

By the way here is the protocol described in the device documentation.

Computer SC 350/360
–––––––> ESC (message start)
–––––––> Command
<––––––> Data (direction depends on command)
<––––––> Check sum (direction depends on command)
<––––––– Receipt:
- ACK (if check sum is correct) or
- NAK (if check sum is incorrect)

And moreover i've here provided the rest of the code with an example of a command send to count the number of coins.

 /// <summary>
    /// Transmit command (no data) on serial channel
    /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <param name="sendCheckSum"></param>
        public void TxCommand(char c1, char c2, bool sendCheckSum)
        {
            Tx(ESC);
            checkSum = 0;
            Tx2((int)c1);
            Tx2((int)c2);
            if (sendCheckSum)
            {
                Tx2(checkSum);
                dummy = RxWait();
            }
        }
        /// <summary>
        /// Read n bytes from serial channel
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public double ReadNumber(int n)
        {
            double number;
            int i;
            number = checkSum = 0;
            for (i = 0; i < n; i++)
                number = number * 256 + RxWait();
            dummy = RxWait();
            return number;
        }

        /// <summary>
        /// Read the number of Coins counted
        /// </summary>
        /// <returns>Number of Coins</returns>
        public double ReadCountReg()
        {
            TxCommand('R', 'C', false);
            double coinsCounted = ReadNumber(4);
            dummy = RxWait();
            return coinsCounted;
        }

To send a command to count coins;

double coinsCounted = ReadCountReg();
 Console.WriteLine(Math.Round(coinsCounted, 0) + " coins counted");

The protocol for reading count register is;

Computer SC 350/360
–––––––> ESC
–––––––> “R”
–––––––> “C”
<––––––– CountRegister (CR)
<––––––– (CR ^ FF00,000016 + CR ^ FF,000016 + CR ^ FF0016 +
CR ^ FF16) ^ FF16
<––––––– ACK
Eliyah
  • 330
  • 4
  • 14

2 Answers2

6

Ugh. 8250 UART registers are documented here. Directly accessing the UART registers in Windows is not supported. Your SerialPort initialization is correct. Beware that it is fairly unlikely your machine has a COM1 port unless you have real hardware. USB emulators tend to pick higher port numbers. Use SerialPort.GetPortNames() to have a look-see.

Tx() waits for transmitter empty status bit. Simply replace with SerialPort.Write() to write one byte, it already blocks if the transmit buffer is full. It won't be.

RxWait() waits for the receiver ready status bit. Simply replace with SerialPort.ReadByte().

Tx2() is just a helper procedure to keep a simple checksum updated, just add the byte you send to a checksum variable.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I see. When you said 'just add the byte you send to a checksum variable', is this correct?; – Eliyah Feb 01 '14 at 15:48
  • It is correct. Presumably you'll need to send that checksum value as the last byte of a message. And set it back to 0 before you start sending the next message. That's usually the way it works anyway. – Hans Passant Feb 01 '14 at 15:50
  • Thanks for the reply. At first I was trying to compile the pascal code using FreePascal but it doesn't support the port array and the go32 unit for win32. If I could get a way to manipulate the ports from FreePascal, in any way possible, I will then compile it to a dll file and then export it to use it from C#.NET using platform invoke. Is there a way I could achieve this?l – Eliyah Feb 02 '14 at 03:04
  • thanks for your help. I've written equivalent C# code for the pascal code and updated in my previous question. Please, with respect, correct me if i get it wrong as I don't have much experience on this. Thanks a lot. – Eliyah Feb 02 '14 at 18:11
4

What's being done here is direct, PC hardware port (specifically COM1) access. For those curious what's going on, base port+5 is the line status register of a 8250 UART and its pincompatible successors like the 16450 and 16550. See here for more details concerning the inner workings of classic PC style serial ports.

Not sure if you'll even ever get this to work properly on Windows (and one thing is certain, it will never ever work with eg serial port dongles that are attached via USB), it's DOS code that partially relies on being quite close to the hardware, have perfect control over timing (DOS was single tasking) and perfectly knowing what hardware to expect. In most cases it should be possible to rely on the facilities that Windows (and in your case, the .Net framework) offer - The stuff you show above is for sending bytes (you can use the Write method for that). The checksum part should be quite trivial to reproduce.

.Net offers an API SerialPort, it should be possible to use that API and do away with this remnant of "good" old DOS days.

fvu
  • 32,488
  • 6
  • 61
  • 79
  • Thanks for the reply. At first I was trying to compile the pascal code using FreePascal but it doesn't support the port array and the go32 unit for win32. If I could get a way to manipulate the ports from FreePascal, in any way possible, I will then compile it to a dll file and then export it to use it from C#.NET using platform invoke. Is there a way I could achieve this? – Eliyah Feb 02 '14 at 02:52
  • And my other question is would my compiled Turbo Pascal program(The pascal code with the port array) work if I run and test it on a windows 7 PC by connecting it to the coin counter machine? As I mentioned earlier I have managed to compile it in Turbo Pascal using DosBox – Eliyah Feb 02 '14 at 03:57
  • @Eliyah no it won't as it won't have direct io port access in Dosbox. – fvu Feb 02 '14 at 10:35
  • So how can I achieve the task in the pascal code using c#? Can you provide provide me a C# code snippet which implements the same task of those procedures? – Eliyah Feb 02 '14 at 17:05
  • @Eliyah refer to Hans Passant's answer, he already gives a list of equivalents. With respect to the checksum, you'll have to analyze how that gets used, and emulate that behavior. – fvu Feb 02 '14 at 17:41
  • thanks a lot. I've edited my original question by writing an equivalent C# code for the pascal program. Please correct me if I happen to get it wrong – Eliyah Feb 02 '14 at 18:25
  • @Eliyah except for the continuous opening/closing of the port it looks good. You can leave the port open as long as your program runs, except for the waste of time closing the port can also lead to hard to debug issues like lost characters, and the closing may have an influence of the port's control signals, which may disturb the device. – fvu Feb 02 '14 at 19:39
  • I appreciate your guidance. So exactly where and when should I close the port? – Eliyah Feb 03 '14 at 01:40
  • @Eliyah somewhere towards the end of the program, just before exiting probably - it really depends a bit on how the application will be organized, if for example you have a menu or other user-action that opens the port, it may be useful to also offer a close option. Otherwise just open the port on program startup and close it on shutdown. – fvu Feb 03 '14 at 08:07
  • Thanks for the response. I was just connecting the machine, using RS232 cable, to a PC and testing the program i wrote. It throws time out error (>250ms) while reading the data using 'port.ReadByte()' in the RxWait method. What could be the problem? I couldn't get far from that. – Eliyah Feb 03 '14 at 09:31
  • @Eliyah see [the documentation](http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.readtimeout%28v=vs.110%29.aspx), SerialPort will throw that exception if the timeout configured for the port is exceeded without receiving anything. You have two choices: set timeout to InfiniteTimeout, or modify your logic to loop as long as the Exception is thrown. The advantage of setting a non-infinite timeout may be that you can avoid your program getting stuck in case of communication troubles, the downside is that some extra logic will be required to loop on the exception. – fvu Feb 03 '14 at 11:00
  • I already tried that with infinite timeout but i just observed that 'port.BytesToRead' is always 0 when I checked it on the RxWait method. I was also trying to check if the DataReceived event fires and that doesn't fire as well. I can't understand what's going on here. – Eliyah Feb 03 '14 at 11:11
  • Without knowing the protocol it's hard to say. Maybe you can install a serial sniffer, [like this one](http://www.serial-port-monitor.com/free-serial-port-monitor-product-details.html) to see whether your program reproduces the dataflow the device expects. – fvu Feb 03 '14 at 11:27
  • I've downloaded the sniffer and the serial device info displays with the Max.TX queue and Max.RX queue set to none. I've updated my question with detailed description of the protocols and an example. Could you please take a look at it and give me a hand here? thanks – Eliyah Feb 03 '14 at 12:08
  • Could you add the info you see in the sniffer? Is there actually some data exchange going on? – fvu Feb 03 '14 at 12:20
  • Max. TX Queue none Max. RX Queue none Max. Baud Rate configurable Provider RS232 Capabilities DTR/DSR, RTS/CTS, RLSD, Parity Check, XON/XOFF, Settable XON/XOFF, Total time-outs, Interval time-outs Settable Parameters Parity, Baud, Data Bits, Stop Bits, Handshaking, Parity Check, RLSD Baud Rates 75, 110, 134.5, 150, 300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 38400, 56K, 115200, 57600, configurable Data Bits 5, 6, 7, 8 Stop Bits 1, 1.5, 2 Parity None, Odd, Even, Mark, Space Current Tx Queue unavailable Current Rx Queue 4,096 bytes (could u maybe add me on skype and discuss?) – Eliyah Feb 03 '14 at 12:25
  • Thanks for all your help i can now start and stop the machine using the c# program. I added 'Thread.Sleep(100)' before writing bytes(Tx method) and reading bytes (RxWait method) and it worked. :) But sending the command to read counted coins, which is shown in my question at the bottom, is not working. The 'ReadNumber' method computes 'number = number * 256 + RxWait()' in a loop as you can see it. The 'RxWait' reads 0 bytes when debugging. Could you please, look at the methods 'ReadNumber' and 'ReadCountReg' and give me a hand on this? These are the issues i am stuck at right now. – Eliyah Feb 03 '14 at 13:27
  • Just re-checked the original code, looks like they enable RTS (via port+4), whereas SerialPort by default doesn't. See [here](http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.rtsenable%28v=vs.110%29.aspx) - I suggest to RtsEnable = true just after opening the port. – fvu Feb 03 '14 at 14:49
  • I have already done that but I wrote that before opening the port. Should I set it to true again after opening the port? By the way I can now Start the machine, Stop it, transfer counted coins to memory and reset counter in memory. What I am left with is Reading coins on the counter and counting coins in memory and few others with same functionality. I have noticed that these procedures receive false checksum status unlike the others. I have updated my question which shows how I implemented the coin counter method and it's protocol if u can get the time to take a look. Thanks a lot for so far – Eliyah Feb 03 '14 at 18:03
  • Pretty certain that RTS command should only be issued on an open port. Regarding the checksum logic, hard to say as your question wasn't updated with the rx routine that tracks the checksum. I also have a hard time interpreting the description of the read count command (`CR ^ FF00,000016 + CR ^ FF,000016 + CR ^ FF0016 + CR ^ FF16) ^ FF16`) - and I think ACK should go back to the device, not from the device as your drawing seems to suggest. – fvu Feb 03 '14 at 18:20
  • The checksum is calculated in the method called 'Tx2' which you will find in the C# code I implemented in my question. I think I should update my question with the whole original pascal code. – Eliyah Feb 03 '14 at 18:32
  • It might be better to restart with a new question, this thing is getting stretched quite a bit beyond its original scope, and thus has less visibility. Now, as I don't have the protocol documentation available I'm just guessing, but are you certain that checksums ae only used when you send to the device, and not to protect data sent to you? That would be very very odd... – fvu Feb 03 '14 at 18:38
  • I think you're right. I might start a new question and you could follow me there. I feel like I am almost there to resolve this. And to answer your question the sample pascal program provided in the technical documentation says that for simplicity the handshake lines are not used and no check sum is implemented. As I have noted earlier in my posts I don't have much experience in the lower level programming area so I don't exactly know the side effect of this. – Eliyah Feb 03 '14 at 18:50
  • Well it wouldn't be the first time that some tech doco claims that the sample doesn't implement checksums for simplicity's sake whereas in reality the device's checksum implementation is just flawed :) Handshaking - OK, but that doesn't mean you don't have to enable RTS all the time to indicate your willingness to receive. – fvu Feb 03 '14 at 18:53
  • I've opened another question with the title 'How to interpret R232C interface communication written using Pascal to C#.Net'. I would really appreciate it if you could get the time to follow me there. Thanks for your help so far. :) – Eliyah Feb 03 '14 at 19:47
  • Hi fvu. Sorry i had to come here again. I got the following result when sniffing the serial port While issuing the 'ReadCountReg' command. (1B 52 43 .RC). There is large space before '.RC' This is the information i got from the 'writes' tab section of the sniffer. There is nothing sniffed in the 'Reads' tab section though. could this be the problem? – Eliyah Feb 04 '14 at 09:04
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/46736/discussion-between-fvu-and-eliyah) – fvu Feb 04 '14 at 10:25