Write/Read command to the specified port - c

I use this usb gpio device. It uses some command to send/receive data from input/output channel. There is a guide that explains how commands send on numato website. There are some sample code for C on Windows. But I use fedora and my code is below for read/write gpio. I can't read data.
Code sample for Windows
#include "stdafx.h"
#include "windows.h"
#include "string.h"
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hComPort;
char cmdBuffer[32];
char responseBuffer[32];
DWORD numBytesWritten;
DWORD numBytesRead;
/*
Lookup the port name associated to your GPIO device and update the
following line accordingly. The port name should be in the format
"\\.\COM<port Number>". Notice the extra slaches to escape slashes
themselves. Read http://en.wikipedia.org/wiki/Escape_sequences_in_C
for more details.
*/
wchar_t PortName[] = L"\\\\.\\COM14";
/*
Open a handle to the COM port. We need the handle to send commands and
receive results.
*/
hComPort = CreateFile(PortName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
if (hComPort == INVALID_HANDLE_VALUE)
{
printf("Error: Unable to open the specified port\n");
return 1;
}
/* EXAMPLE 1 - MANIPULATE GPIO0 BY SENDING COMMAND */
/**************************************************************************
Send a command to output a logic high at GPIO 0. The command that is
used to accomplish this acton is "gpio set 0". It is important to send
a Carriage Return character (ASCII value 0x0D) to emulate the ENTER
key. The command will be executed only when the GPIO module detects
Carriage Return character.
**************************************************************************/
/* Write a Carriage Return to make sure that any partial commands or junk
data left in the command buffer is cleared. This step is optional.
*/
cmdBuffer[0] = 0x0D;
if(!WriteFile(hComPort, cmdBuffer, 1, &numBytesWritten, NULL))
{
CloseHandle(hComPort);
printf("Error: Unable to write to the specified port\n");
return 1;
}
/* Copy the command to the command buffer */
strcpy(cmdBuffer, "gpio set 0");
/* Append 0x0D to emulate ENTER key */
cmdBuffer[10] = 0x0D;
/* Write the command to the GPIO module. Total 11 bytes including 0x0D */
printf("Info: Writing command <gpio set 0> to the GPIO module\n");
if(!WriteFile(hComPort, cmdBuffer, 11, &numBytesWritten, NULL))
{
CloseHandle(hComPort);
printf("Error: Unable to write to the specified port\n");
return 1;
}
printf("Info: <gpio set 0> Command sent successfuly\n");
/* EXAMPLE 2 - MANIPULATE GPIO10 BY SENDING COMMAND */
/**************************************************************************
Send a command to output a logic high at GPIO 0. The command that is
used to accomplish this acton is "gpio set 0". It is important to send
a Carriage Return character (ASCII value 0x0D) to emulate the ENTER
key. The command will be executed only when the GPIO module detects
Carriage Return character.
**************************************************************************/
/* Write a Carriage Return to make sure that any partial commands or junk
data left in the command buffer is cleared. This step is optional.
*/
cmdBuffer[0] = 0x0D;
if(!WriteFile(hComPort, cmdBuffer, 1, &numBytesWritten, NULL))
{
CloseHandle(hComPort);
printf("Error: Unable to write to the specified port\n");
return 1;
}
/*
Copy the command to the command buffer. GPIO number 10 and beyond are
referenced in the command by using alphabets starting A. For example
GPIO10 willbe A, GPIO11 will be B and so on. Please note that this is
not intended to be hexadecimal notation so the the alphabets can go
beyond F.
*/
strcpy(cmdBuffer, "gpio set A");
/* Append 0x0D to emulate ENTER key */
cmdBuffer[10] = 0x0D;
/* Write the command to the GPIO module. Total 11 bytes including 0x0D */
printf("Info: Writing command <gpio set A> to the GPIO module\n");
if(!WriteFile(hComPort, cmdBuffer, 11, &numBytesWritten, NULL))
{
CloseHandle(hComPort);
printf("Error: Unable to write to the specified port\n");
return 1;
}
printf("Info: <gpio set A> Command sent successfuly\n");
/* EXAMPLE 3 - READ ADC 1 */
/**************************************************************************
Write "adc read 1" comamnd to the device and read back response. It is
important to note that the device echoes every single character sent to
it and so when you read the response, the data that is read will
include the command itself, a carriage return, the response which you
are interested in, a '>' character and another carriage return. You
will need to extract the response from this bunch of data.
/*************************************************************************/
/* Write a Carriage Return to make sure that any partial commands or junk
data left in the command buffer is cleared. This step is optional.
*/
cmdBuffer[0] = 0x0D;
if(!WriteFile(hComPort, cmdBuffer, 1, &numBytesWritten, NULL))
{
CloseHandle(hComPort);
printf("Error: Unable to write to the specified port\n");
return 1;
}
/* Flush the Serial port's RX buffer. This is a very important step*/
Sleep(10);
PurgeComm(hComPort, PURGE_RXCLEAR|PURGE_RXABORT);
/* Copy the command to the command buffer */
strcpy(cmdBuffer, "adc read 1");
/* Append 0x0D to emulate ENTER key */
cmdBuffer[10] = 0x0D;
/* Write the command to the GPIO module. Total 11 bytes including 0x0D */
printf("Info: Writing command <adc read 1> to the GPIO module\n");
if(!WriteFile(hComPort, cmdBuffer, 11, &numBytesWritten, NULL))
{
CloseHandle(hComPort);
printf("Error: Unable to write to the specified port\n");
return 1;
}
printf("Info: <adc read 1> Command sent successfuly\n");
/*Read back the response*/
if(!ReadFile(hComPort, responseBuffer, 16, &numBytesRead, NULL))
{
CloseHandle(hComPort);
printf("Error: Unable to write to the specified port\n");
return 1;
}
/* Add a null character at the end of the response so we can use the buffer
with string manipulation functions.
*/
responseBuffer[numBytesRead] = '\0';
printf("Info: ADC value read from the device = %.*s", 4, responseBuffer + 12);
/* Close the comm port handle */
CloseHandle(hComPort);
return 0;
}
My code for Ubuntu
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#define MAXBUFFER 32
int main(int argc, char* argv[])
{
struct termios serialSettings;
char *deviceName = "/dev/ttyACM0";
char bufferRecv[MAXBUFFER], bufferSend[MAXBUFFER];
int readInt, sendInt;
int fd = open(deviceName, O_RDWR | O_NOCTTY | O_NDELAY);
if(fd == -1) {
printf("\n %s\n", deviceName);
perror("unable to open port");
}
else {
printf("port is opened!\n");
bufferSend[0] = 0x0D; /* clear buffer */
strcpy(bufferSend, "gpio set 0"); /* */
sendInt = write(fd, bufferSend, strlen(bufferSend));
if(sendInt <= 0){
printf("Unable to write to the port\n");
return -1;
}
printf("<gpio set 0> : Command sent successfuly\n");
strcpy(bufferSend, "gpio read 0"); /* */
sendInt = write(fd, bufferSend, strlen(bufferSend));
if(sendInt <= 0){
printf("Unable to write to the port\n");
return -1;
}
printf("<gpio read 0> : Command sent successfuly\n");
readInt = read(fd, bufferRecv, sizeof(bufferRecv));
if(readInt < 0){
printf("Unable to read to the port\n");
return -1;
}
bufferRecv[strlen(bufferRecv)] = '\0';
printf("read=%c-\n", bufferRecv[0]);
}
close(fd);
return 0;
}
The output
port is opened!
<gpio set 0> : Command sent successfuly
<gpio read 0> : Command sent successfuly
Unable to read to the port

The "successful" write() s are false positives. The data was output, but not properly received by the device.
Your program has not properly configured the serial port using termios after the open() and before any write() or read(). You should configure the port for canonical rather than raw mode. You also need to configure the baud rate, character length, parity and number of stop bits.
Use the Serial Programming Guide for POSIX Operating Systems.
Workable(?) configuration code might look like (for 115200 baud rate, 8N1 and canonical input, i.e. read terminates on NL character):
#include <termios.h>
struct termios serialSettings;
speed_t spd;
int fd;
int rc;
fd = open(deviceName, O_RDWR | O_NOCTTY);
if (fd == -1) {
printf("\n %s\n", deviceName);
perror("unable to open port");
return -1;
}
rc = tcgetattr(fd, &serialSettings);
if (rc < 0) {
perror("unable to get attributes");
return -2;
}
spd = B115200;
cfsetospeed(&serialSettings, spd);
cfsetispeed(&serialSettings, spd);
serialSettings.c_cflag &= ~CSIZE;
serialSettings.c_cflag |= CS8;
serialSettings.c_cflag &= ~PARENB;
serialSettings.c_cflag &= ~CSTOPB;
serialSettings.c_cflag &= ~CRTSCTS; /* no HW flow control? */
serialSettings.c_cflag |= CLOCAL | CREAD;
serialSettings.c_iflag &= ~(PARMRK | ISTRIP | IXON | IXOFF | INLCR);
serialSettings.c_iflag |= ICRNL;
serialSettings.c_oflag &= ~OPOST;
serialSettings.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
serialSettings.c_lflag |= ICANON;
rc = tcsetattr(fd, TCSANOW, &serialSettings);
if (rc < 0) {
perror("unable to set attributes");
return -2;
}
/* serial port is now configured and ready */
...
Additional comments on your code:
bufferRecv[strlen(bufferRecv)] = '\0';
This is illogical code. If you could actually determine the string length of what's in bufferRecv, then that text would already be null terminated, and this assignment would not be necessary.
Secondly and much worse, the read() does not terminate the receive data with a null byte, so the strlen() could scan past the buffer end.
The read() does return the number of bytes stored in the buffer, and that value can be used to locate where a null byte should be written.
Workable code would look like (note the reduction in requested read length):
readInt = read(fd, bufferRecv, sizeof(bufferRecv) - 1);
if (readInt < 0){
perror("Unable to read from the port\n");
return -3;
}
bufferRecv[readInt] = '\0';
printf("ADC value read = %s\n", bufferRecv);
The strings that you send to the USB device should be preceded and terminated with a carriage return just like the Win examples:
strcpy(bufferSend, "\rgpio set 0\r");
...
strcpy(bufferSend, "\rgpio read 0\r");

Related

Receive from UART on Pi

I'm new here.
I'm trying receive data from UART which is from Xbee.
The sample code below is allow me to check the API frame.
But what I need is make these to be a useful array.
How should i make this into a complete array?
The result shows what i need but they are independent.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
int main(int argc, char const *argv[])
{
//-------------------------
//----- SETUP USART 0 -----
//-------------------------
//At bootup, pins 8 and 10 are already set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively
int uart0_filestream = -1;
//OPEN THE UART
//The flags (defined in fcntl.h):
// Access modes (use 1 of these):
// O_RDONLY - Open for reading only.
// O_RDWR - Open for reading and writing.
// O_WRONLY - Open for writing only.
//
// O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
// if there is no input immediately available (instead of blocking). Likewise, write requests can also return
// immediately with a failure status if the output can't be written immediately.
//
// O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.
uart0_filestream = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
if (uart0_filestream == -1)
{
//ERROR - CAN'T OPEN SERIAL PORT
printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
}
struct termios options;
tcgetattr(uart0_filestream, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uart0_filestream, TCIFLUSH);
tcsetattr(uart0_filestream, TCSANOW, &options);
//----- TX BYTES -----
unsigned char tx_buffer[20];
unsigned char *p_tx_buffer;
p_tx_buffer = &tx_buffer[0];
*p_tx_buffer++ = 'H';
*p_tx_buffer++ = 'e';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'o';
if (uart0_filestream != -1)
{
int count = write(uart0_filestream, &tx_buffer[0], (p_tx_buffer - &tx_buffer[0]));
//Filestream, bytes to write, number of bytes to write
if (count < 0)
{
printf("UART TX error\n");
}
}
char r[100];
while(1){
//----- CHECK FOR ANY RX BYTES -----
if (uart0_filestream != -1)
{
//printf("OK");
// Read up to 255 characters from the port if they are there
unsigned char rx_buffer[256];
int rx_length = read(uart0_filestream, (void*)rx_buffer, 255); //Filestream, buffer to store in, number of bytes to read (max)
if (rx_length < 0)
{
//An error occured (will occur if there are no bytes)
}
else if (rx_length == 0)
{
//No data waiting
}
else
{
//Bytes received
rx_buffer[rx_length] = '\0';
printf("%i bytes read : %s\n", rx_length, rx_buffer);
//printf("%d\n",rx_buffer[0]);
}
}
}
return 0;
}

Entire characters in the text file is not getting transmitted in Hi 3520D

I am trying to read some data from a text file and writing it to the ttyUSB* socket id.
I am using Hi3520d Dvr. I have it's RS485 port connected to a "RS485 to RS232 converter". This converter is connected to the PC through a USB port.
The text file is getting read properly to the buffer, but while writing last few lines of the text is not transmitting. This is happening with file with size more than 4.5kb exactly and without usleep() function.
I am using minicom on linux terminal to display both read and written text.
Thanks in advance for looking into this.
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define Bdrate B9600
int Intserial(char *dev, int Baudrate)
{
//printf("Insterial func\n");
int sid;
int iDebug = -1;
struct termios serial_struct;
sid = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
if (sid > 0)
{
memset(&serial_struct, 0, sizeof(serial_struct)); /* clear the new struct */
serial_struct.c_cflag = Baudrate | CS8 | CLOCAL | CREAD;
serial_struct.c_iflag = IGNPAR;
serial_struct.c_oflag = 0;
serial_struct.c_lflag = 0;
serial_struct.c_cc[VMIN] = 0; /* block untill n bytes are received */
serial_struct.c_cc[VTIME] = 0; /* block untill a timer expires (n * 100 mSec.) */
iDebug = tcsetattr(sid, TCSANOW, &serial_struct);
if (iDebug < 0)
{
printf("Err 0\n"); //Unable to set serial port parameters
return (0);
}
}
else
{
printf("Err 1\n"); //Serial port not open
return (0);
}
//printf("sid is %d \n",sid);
return (sid);
}
int main()
{
int sid1 = -1, size = 0, i = 0, x, w;
size_t ln;
FILE *fd;
char buf[2233];
fd = fopen("h.txt", "r");
if (fd)
{
sid1 = Intserial("/dev/ttyAMA1", Bdrate); //RS485 port of Hi3520d
if (sid1 > -1)
{
system("himm 0x200F004C 0"); // commands transmitting and recieving
system("himm 0x201A0400 1");
system("himm 0x201a0004 1");
while (!feof(fd))
{
memset(buf, 0, sizeof(buf));
fread(buf, sizeof(buf), 1, fd);
printf("%s", buf);
write(sid1, buf, sizeof(buf));
usleep(5);
}
getchar();
}
else
printf("com port cant open\r\n ");
fclose(fd);
close(sid1);
}
else
printf("File cant open\r\n");
printf("task completed............\r\n");
}
You have to observe return value of fread for number of bytes read by fread function. the actual read size may not equal to bytes requested, also you have to pass number of bytes read by fread (as valid bytes in buffer) to write function as number of bytes to write.
The code should be something like this
memset(buf,0,sizeof(buf));
size_t bytesRead = fread(buf,sizeof(buf),1,fd);
if(bytesRead > 0)
write(sid1,buf, bytesRead);
Also as LPs said, fread doesn't end buffer with termination character, so passing buffer filled by fread to printf("%s") will be undefined behavior
There are numerous issues with your code, but the salient cause of "the text is not transmitting" is probably the failure to check the return value of
write(sid1, buf, sizeof(buf));
Because the serial terminal was opened in non-blocking mode, each write() will return immediately, before the data has been actually transmitted.
Since the serial terminal is configured for a rather slow 9600 baud, the data could be queued up in the line discipline buffer and other intermediate buffers.
The line discipline buffer is typically 4096 bytes long.
Assuming that the fread() operations are always successful (which you seem to have verified), then the second iteration of the write() of 2233 bytes could potentially saturate the line discipline buffer, and return with a short write return value (which would be ignored).
The third iteration of the write(), if it's quick enough, could then be outright rejected with a return value of -1 and an errno of EAGAIN to indicate that the write would block.
This error condition would be silently ignored, and this 2233 bytes of data will never be transmitted.
This seems to correlate perfectly with your observation of "last few lines of the text is not transmitting ... with file with size more than 4.5kb exactly and without usleep() function."
ADDENDUM
Revised code for blocking mode, proper terminal setup, and checking of return values is shown below.
A corrected version of #e.jahandar's suggestion and comments from #LPs are also incorporated.
...
sid = open(dev, O_RDWR | O_NOCTTY);
if (sid < 0) {
printf("Err 1\n"); //Serial port not open
return (-1);
}
if (tcgetattr(sid, &serial_struct) < 0) {
printf("Err 2\n");
return (-2);
}
cfsetospeed(&serial_struct, (speed_t)Baudrate);
cfsetispeed(&serial_struct, (speed_t)Baudrate);
cfmakeraw(&serial_struct);
serial_struct.c_cc[VMIN] = 1;
serial_struct.c_cc[VTIME] = 10;
serial_struct.c_cflag &= ~CSTOPB;
serial_struct.c_cflag &= ~CRTSCTS; /* no HW flow control? */
serial_struct.c_cflag |= CLOCAL | CREAD;
if (tcsetattr(sid, TCSANOW, &serial_struct) < 0) {
printf("Err 3\n"); //Unable to set serial port parameters
return (-3);
}
...
#define BUFSIZE 2233
char buf[BUFSIZE + 1];
...
size_t frv;
ssize_t wrv;
...
do {
frv = fread(buf, 1, BUFSIZE, fd);
buf[frv] = 0; /* terminate string for printf */
if (frv > 0) {
wrv = write(sid1, buf, frv);
if (wrv < frv) {
/* handle error or short write */
}
} else
break;
} while (1);
...

How to end program with period (.)

The user entries different characters: uppercase and lowercase letters and numbers. Entering ends with a period (.) The program should then calculate and print sum of all entered numbers, and ignore everything else. Program won't end with a period (.)
#include <stdio.h>
int main() {
int s=0, a;
char n;
printf("Unesite znakove: ");
for ( ; ; ) {
scanf ("%c", &n);
if (n!='.') {
a=(int)n;
if (a>47 && a<58) {
a=a-48;
s+=a;
}
else continue;
}
else {
break;
}
}
printf ("\nSuma je: %d", s);
return 0;
}
stdin is usually line buffered.
Code does not see any input until '\n' is entered. Entering "123." is not enough. Code needs a final Enter: '\n' (or closure of stdin).
the stdin is being input in 'cooked' mode, which means nothing is actually visible to the program until either EOF or is entered by the user.
If you want the data to be available to the program, keystroke by keystroke, then the terminal needs to be in 'raw' mode
the following code demonstrates how to set the terminal into 'raw' mode
Note: code swiped from: http://www.minek.com/files/unix_examples/raw.html
#include <stdio.h>
#include <signal.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
struct termios oldtermios;
int ttyraw(int fd)
{
/* Set terminal mode as follows:
Noncanonical mode - turn off ICANON.
Turn off signal-generation (ISIG)
including BREAK character (BRKINT).
Turn off any possible preprocessing of input (IEXTEN).
Turn ECHO mode off.
Disable CR-to-NL mapping on input.
Disable input parity detection (INPCK).
Disable stripptcsetattr(fd, TCSAFLUSH, &newtermios)ing of eighth bit on input (ISTRIP).
Disable flow control (IXON).
Use eight bit characters (CS8).
Disable parity checking (PARENB).
Disable any implementation-dependent output processing (OPOST).
One byte at a time input (MIN=1, TIME=0).
*/
struct termios newtermios;
if(tcgetattr(fd, &oldtermios) < 0)
return(-1);
memcpy( newtermios, oldtermios, sizeof(struct termios) );
newtermios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* OK, why IEXTEN? If IEXTEN is on, the DISCARD character
is recognized and is not passed to the process. This
character causes output to be suspended until another
DISCARD is received. The DSUSP character for job control,
the LNEXT character that removes any special meaning of
the following character, the REPRINT character, and some
others are also in this category.
*/
newtermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* If an input character arrives with the wrong parity, then INPCK
is checked. If this flag is set, then IGNPAR is checked
to see if input bytes with parity errors should be ignored.
If it shouldn't be ignored, then PARMRK determines what
character sequence the process will actually see.
When we turn off IXON, the start and stop characters can be read.
*/
newtermios.c_cflag &= ~(CSIZE | PARENB);
/* CSIZE is a mask that determines the number of bits per byte.
PARENB enables parity checking on input and parity generation
on output.
*/
newtermios.c_cflag |= CS8;
/* Set 8 bits per character. */
newtermios.c_oflag &= ~(OPOST);
/* This includes things like expanding tabs to spaces. */
newtermios.c_cc[VMIN] = 1;
newtermios.c_cc[Vtcsetattr(fd, TCSAFLUSH, &newtermios)TIME] = 0;
/* You tell me why TCSAFLUSH. */
if(tcsetattr(fd, TCSAFLUSH, &newtermios) < 0)
return(-1);
return(0);
}
int ttyreset(int fd)
{
if(tcsetattr(fd, TCSAFLUSH, &oldtermios) < 0)
return(-1);
return(0);
}
void sigcatch(int sig)
{
ttyreset(0);
exit(0);
}
void main()
{
int i;
char c;
/* Catch the most popular signals. */
if((int) signal(SIGINT,sigcatch) < 0)
{
perror("signal");
exit(1);
}
if((int)signal(SIGQUIT,sigcatch) < 0)
{
perror("signal");
exit(1);
}
if((int) signal(SIGTERM,sigcatch) < 0)
{
perror("signal");
exit(1);
}
/* Set raw mode on stdin. */
if(ttyraw(0) < 0)
{
fprintf(stderr,"Can't go to raw mode.\n");
exit(1);
}
while( (i = read(0, &c, 1)) == 1)
{
if( (c &= 255) == 0177) /* ASCII DELETE */
break;
printf( "%o\n\r", c);
}
if(ttyreset(0) < 0)
{
fprintf(stderr, "Cannot reset terminal!\n");
exit(-1);
}tcsetattr(fd, TCSAFLUSH, &newtermios)
if( i < 0)
{
fprintf(stderr,"Read error.\n");
exit(-1);
}
exit(0);
}
the main criteria is to declare a struct termios, use tcgetattr(fd, &oldtermios) to acquire the current terminal settings, (for safety, now make a copy of the original terminal settings.) copy oldtermios to a newtermios and modify the newtermios settings for the desired mode, then use tcsetattr(fd, TCSAFLUSH, &newtermios) to modify the terminal to the 'raw' mode.
When done, be sure to call tcsetattr(fd, TCSAFLUSH, &oldtermios); to return the terminal mode to 'cooked'
Note: signal() is obsolete/unreliable, replace with sigaction()

# of Bytes at Serial Port Input Buffer

New to C programming here so bear with me.. I wrote a program to write/read data to and from the serial port. Everything appears to be working except when I try to read the number of bytes available at the port that have been received. Here's my code (see the read_port function):
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/ioctl.h> /* Serial Port IO Controls */
int fd; /* File descriptor for the port */
struct termios options_original; /* Original Serial Port Options */
int main()
{
fd = open_port();
flush_port();
write_port();
printf("FIONBIO value %d\n", FIONBIO);
usleep(2);
printf("FIONREAD value %d\n", FIONREAD);
read_port();
close_port();
}
/*
* open_port() - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error
*/
int open_port(void)
{
struct termios options;
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
if (fd != -1)
{
printf("Serial Port Open\n");
fcntl(fd, F_SETFL, 0);
tcgetattr(fd, &options_original);
tcgetattr(fd, &options);
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
options.c_cflag |= (CLOCAL | CREAD); /* Enable the receiver and set local mode */
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Raw Input Mode */
tcsetattr(fd, TCSANOW, &options); /* Set the new options for the port */
}
else
{
/* Could not open the port */
perror("open_port: Unable to open /dev/ttyUSB0 - ");
}
return (fd);
}
int close_port(void)
{
tcsetattr(fd, TCSANOW, &options_original);
printf("Serial Port Closed\n");
close(fd);
}
int flush_port(void)
{
usleep(2); // required to make flush work, for some reason
printf("Flushing IO Buffers\n");
tcflush(fd, TCIOFLUSH);
}
int write_port(void)
{
int n = write(fd, "DSC", 3);
if (n < 0)
fputs("write() of 1 byte failed!\n", stderr);
else
printf("Wrote %0d bytes to serial port\n", n);
}
int read_port(void)
{
int chars_read = 3;
int bytes;
char read_buffer[3] = {0};
int i;
fcntl(fd, F_SETFL, 0);
ioctl(fd, FIONBIO, &bytes);
printf("Number of bytes = %d\n", bytes);
int n = read(fd, read_buffer, chars_read);
printf("Character at Port: %s\n", read_buffer);
printf("Number of chars read = %0d\n", n);
}
Here's the output:
Serial Port Open
Flushing IO Buffers
Wrote 3 bytes to serial port
FIONBIO value 21537
FIONREAD value 21531
Number of bytes = 0
Character at Port: DSC
Number of chars read = 3
Serial Port Closed
For some reason 'Number of bytes' always equals 0. I have no idea why. Is there something wrong with doing this?
int bytes;
ioctl(fd, FIONBIO, &bytes);
printf("Number of bytes = %d\n", bytes);
It's pretty much verbatim from this site:
http://www.cmrr.umn.edu/~strupp/serial.html#config
Am I missing or not understanding something?
BTW I'm just doing a simple loopback test here.
ioctl(fd, FIONBIO, ...) sets a file descriptor (e.g. a socket) into blocking or
non-blocking mode. What you probably meant is
ioctl(fd, FIONREAD, &bytes);
to get the number of bytes available in the input buffer (i.e. the number of bytes
that can be read without blocking.)

Reading SMS messages from sim via serial port in Linux using C

am trying to read sms messages via serial port in linux a from a sim card which i have placed inside a huawei 3g usb modem. i have to execute the script a number of time before some of the sms messages are displayed on the screen. At times it displays unusual characters. All i want to do is read sms messages from the sim using AT commands, c and serial port. Below is the code i am using.
int main(){
int fd;
struct termios options;
/* open the port */
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{ /* Could not open the port */
fprintf(stderr, "open_port: Unable to open /dev/ttyS1 - %s\n",strerror(errno));
}else{
printf("port opened\n");
}
fcntl(fd, F_SETFL, 0);
/* get the current options */
tcgetattr(fd, &options);
/* set raw input, 1 second timeout */
options.c_cflag |= (CLOCAL | CREAD);
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_oflag &= ~OPOST;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
/* set the options */
tcsetattr(fd, TCSANOW, &options);
char buffer[400]; /* Input buffer */
char *bufptr; /* Current char in buffer */
int nbytes; /* Number of bytes read */
int tries; /* Number of tries so far */
for (tries = 0; tries < 1; tries ++)
{
/* send an AT command*/
if (write(fd, "AT+CMGL=\"ALL\"\r", strlen("AT+CMGL=\"ALL\"\r")) < 3){
printf("command sent\n");
continue;
}
/* read characters into our string buffer*/
bufptr = buffer;
nbytes = read(fd, bufptr, buffer + sizeof(buffer) - bufptr - 1);
printf("%s\n",bufptr);
char *p;
p = strstr(buffer, "tin");
printf("%s",p);
p = strstr(buffer, "server");
if(p == NULL) printf("not from server\n");
*bufptr = '\0';
}
return 0;
}
Appearently Gnokii project supports Huawei devices -- http://wiki.gnokii.org/index.php/Huawei
I'd either go with gnokii, it's this simple:
$ gnokii --getsms
or at least look into gnokii sources, because the problem you're describing looks certainly like a synchronization or waiting-for-output problem and they almost certainly already have a nice and tested solution for that.
First of all, you need to be checking the return value of read. Basically read is never guaranteed to give you what you want. It might return early with nothing read due to your timeout or being interrupted by a signal (although you don't have any signal handlers so that's not an issue here) or give you just a partial read. You need to use the return value to advance your buffer pointer and read more until you determine you've read all the data you're looking for.
Aside from that, you should probably not just look at fixed text in an SMS to confirm the sender's id. I would sign the SMS with public key cryptography if you need to know it really came from the server you think sent it...

Resources