14 Replies - 1174 Views - Last Post: 31 July 2012 - 01:57 PM Rate Topic: -----

#1 wd40bomber7  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 87
  • Joined: 22-November 09

Linux Com Ports

Posted 24 July 2012 - 11:49 AM

I have a linux computer and a windows computer trying to communicate over a com port. The linux computer is using a usb 2 serial converter and the windows computer has a built in com port. Furthermore the linux code is written in C++ and the windows code is in C#. Since I think it is far more difficult to debug the linux code, I've posted this in the C++ forum.

The windows computer can send information to the linux computer over the serial port no problem. However whenever I call Write() on the linux computer nothing seems to happen.

Here is the linux code:
//This code opens the serial port. Sorry for some of the run around with variables. I planned on fixing it later on
int fd;
string serialName = settings->GetSettingStr("arduino.SerialPortName");
struct termios options;
int port;

memset(&options,0,sizeof(options));

this->worldState = worldState;
this->settings = settings;
worldState->LastArduinoPacketTime = Time::Now(); //This gives the arduino some time to respond

if ((port = open(serialName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY)) < 0) {
	throw -1;
}
serialPortHandle = fd = port; //Uhm I think this is how this works?

dataBuffer = new Ring<byte>(1024); //Up to a kilobyte of data in the ring buffer!

//Allow read operation to be non-blocking. (Hurray!)
fcntl(fd, F_SETFL, FNDELAY);

if (tcgetattr(fd,&options) != 0) {
	throw -99; //TODO: Make this error message valid
}

cfsetispeed(&options, settings->GetSettingInt("arduino.Baud"));
cfsetospeed(&options, settings->GetSettingInt("arduino.Baud"));

options.c_cflag = (options.c_cflag & ~CSIZE) | CS8;
options.c_iflag &= IGNBRK;
options.c_lflag = 0;
options.c_oflag = 0;
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~(PARENB | PARODD);
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CRTSCTS;


if (tcsetattr(fd, TCSANOW, &options) != 0) {
	throw -99; //TODO: Make this error message valid
}

serialPortHandle = fd;
serialPortRunning=true;

//This code is in a different method and is what is doing the writing. Setting a breakpoint here verifies it is being called correctly
if (write(serialPortHandle,packet,length) < 0) {
	Console::Log << "\nALR 005 Serial port connection write failed.\n";
	serialPortRunning=false;
}



And Here's the C# code:
//This opens the port on the windows side
openedPort = new SerialPort(whichSerial, baud, Parity.None, 8, StopBits.One);
openedPort.ReceivedBytesThreshold = 1;
openedPort.Open(); //Exceptions handled by next layer up.
if (!openedPort.IsOpen)
	throw new Exception("Unable to open serial port.");
openedPort.DataReceived += DataReceivedHandler;
this.callingForm = callingForm;
this.sim = sim;

//This never gets called
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
}




I've identified 5 different properties which need to be the same for serial ports to work:
Handshake type,
Baud,
Parity,
Data bits,
Stop bit(s).

The serial port can receive information sent from the windows computer no problem, so as far as I know most of these must be correct. However, something is preventing the linux computer from sending any data, or the windows computer from receiving any data.

This post has been edited by wd40bomber7: 24 July 2012 - 11:54 AM


Is This A Good Question/Topic? 0
  • +

Replies To: Linux Com Ports

#2 GWatt  Icon User is offline

  • member icon

Reputation: 257
  • View blog
  • Posts: 3,035
  • Joined: 01-December 05

Re: Linux Com Ports

Posted 24 July 2012 - 12:01 PM

Is it possible for you to test the C++ code against itself. i.e., have two linux machines communicate using it and then test the C# code against itself as well? Not having to test two different codebases against each other should help narrow down bugs. Is it possible to use a serial terminal like RealCom or HyperTerminal to test your applications?
Was This Post Helpful? 0
  • +
  • -

#3 jimblumberg  Icon User is online

  • member icon

Reputation: 3060
  • View blog
  • Posts: 9,309
  • Joined: 25-December 09

Re: Linux Com Ports

Posted 24 July 2012 - 12:06 PM

How are you insuring that you are able to communicate properly with either computer? Have you tried to communicate using a "terminal" program on one side of the link. For example using something like minicom on the Linux computer to test your Windows C# program? You may also want to insure that you can communicate between computers using a "terminal" program on both sides of the link, this will insure that your cable is correct and your settings are identical.

For the Linux computer this link may be of some value: Serial Programming Guide.


Jim
Was This Post Helpful? 1
  • +
  • -

#4 wd40bomber7  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 87
  • Joined: 22-November 09

Re: Linux Com Ports

Posted 24 July 2012 - 12:39 PM

To clarify:
Running gtkterm on linux and termite on windows (both serial console applications) works fully in both directions.
Running gtkterm on linux and my C# program on windows works fully in both directions.
Running my C++ program on linux and my C# program on windows works in one direction only. (Windows can send to linux, but linux can't send back)

No I can't test with two linux computers because I do not have a second linux computer to work with and for various reasons I can't convert the windows computer to linux, nor even run a linux VM. Furthermore the two applications are different. I would have to re-write the C# application in C++ to set up such a test.

I'm quite confused at this point unfortunately. Also I've discovered that Handshake is equivalent to Flowcontrol
Was This Post Helpful? 0
  • +
  • -

#5 GWatt  Icon User is offline

  • member icon

Reputation: 257
  • View blog
  • Posts: 3,035
  • Joined: 01-December 05

Re: Linux Com Ports

Posted 24 July 2012 - 12:41 PM

What about running termite and your C++ program? what does that yield?
Was This Post Helpful? 1
  • +
  • -

#6 wd40bomber7  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 87
  • Joined: 22-November 09

Re: Linux Com Ports

Posted 24 July 2012 - 01:06 PM

Unfortunately testing it with termite and my C++ program wasn't possible because of the way my program was made. So I made a second simple program for that exact test, and it worked first try. I have no idea why, but for some reason everything functions now. It's nice that it's functional and frustrating that it wasn't functional several hours ago.

Anyways, thank you for all your suggestions everyone.
Was This Post Helpful? 0
  • +
  • -

#7 jimblumberg  Icon User is online

  • member icon

Reputation: 3060
  • View blog
  • Posts: 9,309
  • Joined: 25-December 09

Re: Linux Com Ports

Posted 24 July 2012 - 01:12 PM

Quote

It's nice that it's functional and frustrating that it wasn't functional several hours ago.

You may want to restart your computers and insure it is still working correctly. Sometimes your terminal programs will properly set your port parameters and your programs may appear to work until the ports are reset to their default states.

Jim
Was This Post Helpful? 1
  • +
  • -

#8 wd40bomber7  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 87
  • Joined: 22-November 09

Re: Linux Com Ports

Posted 26 July 2012 - 09:11 AM

I know this thread is several days old. But you couldn't have been more right. That's exactly what seems to have happened. I just rebooted and now it doesn't work anymore. When I open up a terminal on windows and connect to a super ismple program, the information sent across becomes super garbled. I'm trying to set 57600bps connection with 8 data bits and 1 stop bit (no handshake/flow control). Any idea what I did wrong?


	unsigned char ping[6] = {'p','i','n','g','\r','\n'};
    int fd;
    struct termios options;

    memset(&options,0,sizeof(options));

    if ((fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY)) < 0) {
        cout << "SAD FACE!\n";
    }

    //Allow read operation to be non-blocking. (Hurray!)
    fcntl(fd, F_SETFL, FNDELAY);

    if (tcgetattr(fd,&options) != 0) {
    	cout << "SAD FACE2!\n";
    }

    cfsetispeed(&options, 57600);
    cfsetospeed(&options, 57600);

    options.c_cflag = (options.c_cflag & ~CSIZE) | CS8;
    options.c_iflag &= IGNBRK;
    options.c_lflag = 0;
    options.c_oflag = 0;
    options.c_iflag &= ~(IXON | IXOFF | IXANY);
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~(PARENB | PARODD);
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CRTSCTS;


    if (tcsetattr(fd, TCSANOW, &options) != 0) {
    	cout << "SAD FACE3!\n";
    }

    cout << "Writing something.\n";
    write(fd,ping,6);
    cout << "That was easy!\n";
    sleep(10000000);


This post has been edited by wd40bomber7: 26 July 2012 - 09:12 AM

Was This Post Helpful? 0
  • +
  • -

#9 jimblumberg  Icon User is online

  • member icon

Reputation: 3060
  • View blog
  • Posts: 9,309
  • Joined: 25-December 09

Re: Linux Com Ports

Posted 26 July 2012 - 10:13 AM

Quote

I'm trying to set 57600bps connection with 8 data bits and 1 stop bit (no handshake/flow control). Any idea what I did wrong?

Part of your problem is probably this:
options.c_cflag = (options.c_cflag & ~CSIZE) | CS8;
   options.c_cflag &= ~(PARENB | PARODD);


Normally you use no parity for 8 bit connections:

// Set port to 8N1
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
// Set 8 bit mode
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;  
// Disable Hardware flow control.
options.c_cflag &= ~CNEW_RTSCTS; 



I also don't recommend combining the options, do each on it's own line. This makes it easier to see where you have made mistakes.

You will also need to set either the raw or Canonical input mode. I suggest the Raw mode:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

And also you may want to turn off software flow control as well.
options.c_iflag &= ~(IXON | IXOFF | IXANY);

And you should probably also set the output mode to the correct mode. Lastly don't forget to set the read timeouts.

The link I provided in my last link mentions all these settings and gives you decent values to start with.


Jim
Was This Post Helpful? 1
  • +
  • -

#10 wd40bomber7  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 87
  • Joined: 22-November 09

Re: Linux Com Ports

Posted 31 July 2012 - 01:09 PM

I have a windows program written in C# and a linux program written in C++ that are communicating to each other through serial.

Whenever I send any information from the windows computer to the linux computer it seems to work except when I send character 0x0D. It is read as 0x0A. I don't know which computer is at fault but I suspect the linux computer which is why I posted here as opposed to the C# forum.

The C# code running on windows
            openedPort = new SerialPort(whichSerial, baud, Parity.None, 8, StopBits.One);
            openedPort.ReceivedBytesThreshold = 1;
            openedPort.Handshake = Handshake.None;
            openedPort.Open(); //Exceptions handled by next layer up.
            if (!openedPort.IsOpen)
                throw new Exception("Unable to open serial port.");
            openedPort.DataReceived += DataReceivedHandler;

        private FileStream fs = File.OpenWrite("angry.hex");
        public void SendPacket(byte[] packet)
        {
            lock (sendDataLock)
            {
                //Though its very unlikely, I still wouldn't want any interruptions!

                //Must calculate simplesum
                packet[0] = (byte)(packet[0] + (SimpleSum.Sum(packet, packet.Length) << 4));
                //Send off packet
                openedPort.Write(packet,0,packet.Length);

                fs.Write(packet, 0, packet.Length);
                fs.Flush();
            }
        }



The C++ code running on linux

    string serialName = settings->GetSettingStr("arduino.SerialPortName");
    struct termios options;
    int serialSpeed = FindBandwidthConstant(settings->GetSettingInt("arduino.Baud"));
    dataBuffer = new Ring<byte>(1024);
    invalidPacketCount = 0;

    memset(&options,0,sizeof(options));

    if ((serialPortHandle = open(serialName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY)) < 0) {
        throw -17;
    }

    //Allow read operation to be non-blocking. (Hurray!)
    fcntl(serialPortHandle, F_SETFL, FNDELAY);

    if (tcgetattr(serialPortHandle,&options) != 0) {
    	throw -18;
    }

    cfsetispeed(&options, serialSpeed);
    cfsetospeed(&options, serialSpeed);

    //Enable 8N1
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    //Disable hardware flow control/handshake
    options.c_cflag &= ~CRTSCTS;

    //Enable raw input
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    //Disable parity checking
    options.c_iflag &= ~(INPCK | ISTRIP);

    //Enable software flowcontrol
    options.c_iflag &= ~(IXON | IXOFF | IXANY);

    //Enable raw output
    options.c_oflag &= ~OPOST;



    if (tcsetattr(serialPortHandle, TCSANOW, &options) != 0) {
    	throw -19;
    }
ofstream errorOut("errorstuff.info",ios::binary | ios::out | ios::trunc);

//This is called over and over again very fast
bool SerialPortOpener::Iterate(void) {
	int dataRead;
	byte buffer[512];

	if (!serialPortRunning)
		return false;

	while ((dataRead = read(serialPortHandle,buffer,sizeof(buffer))) > 0) {
		for (int i = 0; i < dataRead;i++) {
			errorOut << buffer[i];
			dataBuffer->push_back(buffer[i]);
		}
	}
	errorOut.flush();
}



When I compare the files angry.hex to errorstuff.info
any 0x0D in angry.hex is a 0x0A in errorstuff.info

Edit: I had it backwards (fixed above)

This post has been edited by wd40bomber7: 31 July 2012 - 01:29 PM

Was This Post Helpful? 0
  • +
  • -

#11 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 719
  • View blog
  • Posts: 1,978
  • Joined: 28-March 11

Re: Linux Com Ports

Posted 31 July 2012 - 01:18 PM

Windows EOL character is 13 (0x0D), Linux EOL character is 10 (0x0A). That probalby why your Linux program converts the 13 to a 10.
Was This Post Helpful? 0
  • +
  • -

#12 wd40bomber7  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 87
  • Joined: 22-November 09

Re: Linux Com Ports

Posted 31 July 2012 - 01:27 PM

That seems unlikely as the linux computer is not aware it is connected to a windows computer. It is simply aware that something is connected on the serial port. I think it is likely I made a mistake implementing the raw-read serial port but whenever I review http://www.easysw.co...ial/serial.html I can't find what I did wrong.

I ran an additional test which pretty much guarantees that the linux code is at fault. I used a terminal to send character 0-255 and 0x0D is replaced by 0x0A by the linux code. No idea why, as far as I can tell I did everything correctly for setting up raw reads.

This post has been edited by wd40bomber7: 31 July 2012 - 01:37 PM

Was This Post Helpful? 0
  • +
  • -

#13 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 1942
  • View blog
  • Posts: 5,778
  • Joined: 05-May 12

Re: Linux Com Ports

Posted 31 July 2012 - 01:36 PM

How are you composing the byte array that you are passing to the C# SendPacket()?

Are you just using UTF8Encoding.GetBytes() or ASCIIEncoding.GetBytes() and the string passed in includes some embedded newlines in them?

Are you seeing a one-for-one replacement of 0x0D for 0x0A, is the C++ stream getting 0x0D 0x0A ?

This post has been edited by Skydiver: 31 July 2012 - 01:37 PM

Was This Post Helpful? 0
  • +
  • -

#14 wd40bomber7  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 87
  • Joined: 22-November 09

Re: Linux Com Ports

Posted 31 July 2012 - 01:38 PM

I am not using any kind of encoding. The data being passed is binary data not text. Also I verified (using a terminal) that the linux code reads 0x0A from 0x0D characters whether or not its being sent from C#

And it's a one-for-one replacement. All 0x0D characters are replaced with 0x0A characters. It is simply impossible to have 0x0D characters.


Edit: Figured it out. For some reason the link above doesn't mention this but to get raw input you need to add

options.c_iflag &= ~(INLCR | ICRNL);

Otherwise you might get weird replacements like I was experiencing.

This post has been edited by wd40bomber7: 31 July 2012 - 01:44 PM

Was This Post Helpful? 4
  • +
  • -

#15 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 1942
  • View blog
  • Posts: 5,778
  • Joined: 05-May 12

Re: Linux Com Ports

Posted 31 July 2012 - 01:57 PM

Ha! Ha! Ha! You must have stumbled on the top Google search hit that I did: http://fixunix.com/l...cr-0x0a-lf.html
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1