Serial communication with 328 on Gertboard


5 posts
by JohnBeardmore » Sat Feb 16, 2013 7:38 pm
I've written a sketch for the 328 which communicated fine via minicom,

minicom -b 9600 -o -D /dev/ttyAMA0

and in /etc/inittab I've commented out the

#TO:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

line, and I've edited out the references to ttyAMA0 in /boot/cmdline.txt

In the code below, the file isn’t opening, and perror is reporting

“Error: Could not open serial device: Too many open files”.

I don’t understand this, as nothing else is having any problem opening files, and minicom is perfectly able to open /dev/ttyAMA0 after my code has failed, and lets me talk to the happy sketch running on the 328.

The key bits of code are shown below. (The open call is in a while loop so that if another app has ttyAMA0 open briefly, this instance will be able to open the device when the first one has finished with it. I don't know if this will work or not, but we're not even getting to one instance opening it at the moment.)

int iFd;
int iErrCount = 0;
char cTxBuff[ 16 ];
char cRxBuff[ 64 ];

while( 1 )
{
iFd = open( "/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NONBLOCK );
if( 0 > iFd )
{
iErrCount++;
if( iErrCount < 5 )
{
sleep( 1 );
continue;
}
else
{
perror( "\nError: Could not open serial device" );
return 3000;
}
}
}


write( iFd, cTxBuff, strlen( cTxBuff ) );
sleep( 1 );
read( iFd, cRxBuff, 32 );

close( iFd );
return 300;
}

Any hints appreciated !

Thanks, J/.
Author of oBeMS open source building / energy management system.
Automatic Meter Reading (AMR), Building Management System (BMS).
See: http://www.T4sLtd.co.uk/oBeMS/ObemsManifestoJb0013.pdf
Posts: 89
Joined: Thu Nov 15, 2012 11:03 pm
Location: Derbyshire UK.
by gordon@drogon.net » Sat Feb 16, 2013 8:53 pm
JohnBeardmore wrote: while( 1 )
{
iFd = open( "/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NONBLOCK );
if( 0 > iFd )
{
iErrCount++;
if( iErrCount < 5 )
{
sleep( 1 );
continue;
}
else
{
perror( "\nError: Could not open serial device" );
return 3000;
}
}
}


This is an infinite loop. The open succeeds, but it loops again - and again, and again, etc.

The error message is correct - too many open files. There is a limit.

Also... Putting the open() in a loop - not that useful - in reality you will open the device or you will fail. There really isn't any point trying to re-try at this level.

Code: Select all
  if ((fd = open ("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NONBLOCK )) < 0)
  {
    perror ("Unable to open serial port") ;
  return EXIT_FAILURE ;
  }


is a more typical way to do it.

Do note the effect of O_NONBLOCK too. All reads will fail if there is no data.

-Gordon
--
Gordons projects: https://projects.drogon.net/
User avatar
Posts: 1520
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
by JohnBeardmore » Sat Feb 16, 2013 9:53 pm
Hi Gordon,

Yes, no sensible way out of the loop except abject failure, by which time more than one thing will have tried to open ttyAMA0 !

>This is an infinite loop.

Not quite. It was exiting via the "return 3000".

>The open succeeds, but it loops again - and again, and again, etc.

Yes. Should I assume from this then, that the OS will only let one process open a tty at a time ? If so, that's good news.

>The error message is correct - too many open files. There is a limit.

Yes, though presumably this is one open() per tty rather than a total number of open files in the system issue. When you google the error message, the material seems to be more about number of files system wide. I assume opening five files couldn't trip a system wide limit in normal circumstances.

>Also... Putting the open() in a loop - not that useful - in reality you will open the device or you will fail.
>There really isn't any point trying to re-try at this level.

I should perhaps explain what I'm trying to do here. The 328 is sitting at the bottom of a stack of modules that comprise the beginnings of an open source building energy management system (oBeMS).

The layer above the 328 (ObemsIoServer) is a little server that listens on port 9800, and forks off a child to access IO on the SPI bus, or talk to the 328 each time a query or instruction is received. This layer can either gather sensor data or drive various actuators.

The top layer does data recording, user interface, decision making, and decision recording. Because everything is accessed via IP addresses and port numbers, the decisions needn't be made on the same boards that run the IO, and the intelligence and analysis can be distributed throughout a network, 'cloud' etc.

In this situation, a number of ObemsIoServer child process will want to talk to the 328 every minute or so, but they will only need to access it for perhaps 20 or 30ms at a time. What I'm trying to do here is set up a situation where if the open() fails, presumably because one of the other processes has it open briefly, the next process to access it can retry a short while later. (Is there any variant of sleep() that works in ms by the way ? A second is a very long time in modern systems !)

Is this sensible, or have I just gone about it completely the wrong way ?

>if ((fd = open ("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NONBLOCK )) < 0)
> {...
>
>is a more typical way to do it.

Yes, but I'd still need to retry later if it failed (unless there's a better way of doing all this). Wouldn't this just push the while loop up to the next layer of code ? In the more typical case you aren't sharing a tty with other devices that only need it for a fraction of a second per minute.

>Do note the effect of O_NONBLOCK too. All reads will fail if there is no data.

Yes, that's fine. I'll check the return value from read(). It should be safe to assume that if the 328 hasn't responded within a second it's died somehow. I guess if this happens I could reset it using a pin on the GPIO, though I haven't looked into that yet.

Anyway Gordon - many thanks for unblocking my thought processes ! It would be embarrassing to admit how many minutes I'd stared at that loop and not spotted the obvious !

Thanks, J/.
Author of oBeMS open source building / energy management system.
Automatic Meter Reading (AMR), Building Management System (BMS).
See: http://www.T4sLtd.co.uk/oBeMS/ObemsManifestoJb0013.pdf
Posts: 89
Joined: Thu Nov 15, 2012 11:03 pm
Location: Derbyshire UK.
by gordon@drogon.net » Sat Feb 16, 2013 10:27 pm
JohnBeardmore wrote:Hi Gordon,

Yes, no sensible way out of the loop except abject failure, by which time more than one thing will have tried to open ttyAMA0 !

>This is an infinite loop.

Not quite. It was exiting via the "return 3000".


Sure - but that'll only happen if you fail to open the serial port - 5 times. You won't fail, so the loop will never exit.

>The open succeeds, but it loops again - and again, and again, etc.

Yes. Should I assume from this then, that the OS will only let one process open a tty at a time ? If so, that's good news.


No. That's not a good assumption to make. Unix/Linux will normally let any number of processes open a file (everything is a file even if it's not, it's treated as a file). If you want to stop another process openeing it, then you need to investigate file locking.

>The error message is correct - too many open files. There is a limit.

Yes, though presumably this is one open() per tty rather than a total number of open files in the system issue. When you google the error message, the material seems to be more about number of files system wide. I assume opening five files couldn't trip a system wide limit in normal circumstances.


The default limit is something like 1024 open files per process. Your loop will hit that limit in under a second.


>Also... Putting the open() in a loop - not that useful - in reality you will open the device or you will fail.
>There really isn't any point trying to re-try at this level.

I should perhaps explain what I'm trying to do here. The 328 is sitting at the bottom of a stack of modules that comprise the beginnings of an open source building energy management system (oBeMS).

The layer above the 328 (ObemsIoServer) is a little server that listens on port 9800, and forks off a child to access IO on the SPI bus, or talk to the 328 each time a query or instruction is received. This layer can either gather sensor data or drive various actuators.

The top layer does data recording, user interface, decision making, and decision recording. Because everything is accessed via IP addresses and port numbers, the decisions needn't be made on the same boards that run the IO, and the intelligence and analysis can be distributed throughout a network, 'cloud' etc.

In this situation, a number of ObemsIoServer child process will want to talk to the 328 every minute or so, but they will only need to access it for perhaps 20 or 30ms at a time. What I'm trying to do here is set up a situation where if the open() fails, presumably because one of the other processes has it open briefly, the next process to access it can retry a short while later. (Is there any variant of sleep() that works in ms by the way ? A second is a very long time in modern systems !)

Is this sensible, or have I just gone about it completely the wrong way ?


If you want many processes to talk to the 328 then you'll need to do 1 of 2 things - either implement file locking, or write one process that talk to the 328 via the serial port, then acts as a multiplexor for other processes talking to it. I'd probably go for the latter mechanism, but it depends on what the other modules are doing and how well you can code inter-process communications - and that leads back to your listner service on port 9800 - maybe that process should be the one to talk to both the serial and SPI and listen to the internet - all at the same time rather than forking off processes (expensive in terms of time & resources).

>if ((fd = open ("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NONBLOCK )) < 0)
> {...
>
>is a more typical way to do it.

Yes, but I'd still need to retry later if it failed (unless there's a better way of doing all this). Wouldn't this just push the while loop up to the next layer of code ? In the more typical case you aren't sharing a tty with other devices that only need it for a fraction of a second per minute.


It's not going to fail. However if it does fail then the chances of it not failing at a later time is remote. Think about why it might fail - permissions is the usual one, but you'll sort that once and it'll be right after that. It could also fail because it runs out of file handles - but that usually indicates a program fault, so if it fails then, you have more problems on you hands... Other reasons for failure may well indicate other more serious system failures - out of memory (why?), hardware IO error (on a serial port? unlikely), etc.

That's not to say you shouldn't ignore failures, but it's more and more leading to the scenario that when you get a failure - any failure - it's really game over.

[/quote]
>Do note the effect of O_NONBLOCK too. All reads will fail if there is no data.

Yes, that's fine. I'll check the return value from read(). It should be safe to assume that if the 328 hasn't responded within a second it's died somehow. I guess if this happens I could reset it using a pin on the GPIO, though I haven't looked into that yet.
[/quote]

it's not just that - if you continually poll read, you'll end up running the Pi at 100% cpu utilisation - and that may be OK in your application, but think about what else the Pi could be doing (it's not a microcontroller - it's much more than that) ... So there are other ways (e.g. setting a timeout on reads - have a look at my wiringSerial module to see how it sets up the serial for for an example) or using the select() or poll() system calls...

Anyway Gordon - many thanks for unblocking my thought processes ! It would be embarrassing to admit how many minutes I'd stared at that loop and not spotted the obvious !

Thanks, J/.


If you want to go down the file locking route (which I don't advise), then look at the man pages for flock(2) - which is advisory, or lockf(3) which can apply blocking locks on files.

Cheers,

-Gordon
--
Gordons projects: https://projects.drogon.net/
User avatar
Posts: 1520
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
by xian » Sat Feb 16, 2013 10:38 pm
Personally, I would have only one process (or section of code) open it once, and then have that part manage interaction with the 328 between any other process that wants to access it. I don't understand your setup well enough to be able to recommend anything more specific.

Edit - Gordon and I just posted at the same time, and he has answered far better than I can.
Posts: 51
Joined: Thu Nov 01, 2012 8:42 pm