I have a very small C program which sends and receives newline-terminated ASCII strings to and from a serial device. It's plugged into my computer with a USB adapter, on /dev/ttyUSB0.
Most of the time it sends the commands just find, but occasionally it will capitalize all the lower-case letters to upper-case. It leaves all special characters alone.
The string I am sending is /home\n. About 1 out of every five times I run the program (by simply running ./a.out without recompiling), the sent message understood by the device is /HOME\n.
Here is my source code:
#include <stdio.h>
#include <stdlib.h>
#include "zserial.h"
int main() {
char buf[256];
int fd = connect("/dev/ttyUSB0");
char *cmd = "/home\n";
send(fd, cmd);
receive(fd, buf, 256);
puts(buf);
exit(0);
}
And zserial.c:
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zserial.h"
int send(int fd, char *buf) {
int len = strlen(buf);
int nb = write(fd, buf, len);
if (len != nb || nb < 1)
perror("Error: wrote no bytes!");
tcdrain(fd);
return nb;
}
int receive(int fd, char *dst, int nbytes) {
int i;
char c;
for(i = 0; i < nbytes;) {
int r = read(fd, &c, 1);
/* printf("Read %d bytes\n", r); */
if (r > 0) {
dst[i++] = c;
if (c == '\n') break;
}
}
dst[i] = 0; /* null-terminate the string */
return i;
}
int connect(char *portname) {
int fd;
struct termios tio;
fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
tio.c_cflag = CS8|CREAD|CLOCAL;
if ((cfsetospeed(&tio, B115200) & cfsetispeed(&tio, B115200)) < 0) {
perror("invalid baud rate");
exit(-1);
}
tcsetattr(fd, TCSANOW, &tio);
return fd;
}
What am I doing wrong? Is there some termios flag which modifies the output on a serial port?
c_oflag & OLCUC turns on the mapping of lowercase to uppercase on output. Since you never initialized tio, it's not surprising you got some random flags set.
You have two choices:
tcgetattr the current settings into a termios struct to initialize it, then modify the ones you're interested in, then write them back with tcsetattr
initialize all the termios fields to known values, not just c_cflag and the speed fields.
Related
I need to read an eeprom in an embedded device.
So far this "almost" worked:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#define READ_SIZE (256)
#define NB_PAGES (128)
void dump_to_file(const char *output_file_path,
const uint8_t *buffer, const int buffer_length)
{
int output_file = open(output_file_path, O_RDWR|O_APPEND|O_CREAT);
if (output_file < 0) {
printf("Failed opening output file %s\n", output_file_path);
return;
}
write(output_file, buffer, buffer_length);
}
int main(int argc, char *argv[])
{
/* got these values from i2cdetect */
const char *i2c_device = "/dev/i2c-4";
const int device_address = 0x50;
/* open the i2c device file */
int file = open(i2c_device, O_RDWR);
if (file < 0) {
printf("Failed opening %s\n", i2c_device);
return 1;
}
if (ioctl(file, I2C_SLAVE, device_address) < 0) {
printf("Failed addressing device at %02X\n", device_address);
close(file);
return 1;
}
int i = 0;
for (i = 0; i < NB_PAGES; i++) {
char buf[READ_SIZE] = {0};
if (read(file, buf, READ_SIZE) != READ_SIZE) {
printf("Failed reading\n");
close(file);
return 1;
}
dump_to_file(argv[1], buf, READ_SIZE);
}
close(file);
return 0;
}
By "almost" I mean that it dumps the full eeprom but the START depends on the last block read..
It's not always the same.
If I read 10 blocks. then run the program again I read the next ones and not the first 10.
How to set the starting address?
Update:
if I do:
i2cset -y 4 0x50 0x00 0x00
and the run the above code, it works.
so how can I put the equivalent of the i2cset command in the code?
Done!
It wasn't easy because I could not find documentations anywhere.. but I thought that since the eeprom is 32K, maybe it was "like" a 24c256. But even in that case I found nothing in userspace until I decided to go by instinct.
I studied i2cset source, understood what it did and put it in the code.
Here is the result, which dumps a full i2c 32k eprom from userspace.
Note a full backup and restore utility can be found here:
https://gist.github.com/Zibri/cf8ac0b311301aeeaa8910c7da824bff
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#define READ_SIZE (256)
#define NB_PAGES (128)
void dump_to_file(const char *output_file_path,
const uint8_t *buffer, const int buffer_length)
{
int output_file = open(output_file_path, O_RDWR|O_APPEND|O_CREAT);
if (output_file < 0) {
printf("Failed opening output file %s\n", output_file_path);
return;
}
write(output_file, buffer, buffer_length);
}
int main(int argc, char *argv[])
{
const char *i2c_device = "/dev/i2c-4";
const int device_address = 0x50;
int file = open(i2c_device, O_RDWR);
if (file < 0) {
printf("Failed opening %s\n", i2c_device);
return 1;
}
if (ioctl(file, I2C_SLAVE, device_address) < 0) {
printf("Failed addressing device at %02X\n", device_address);
close(file);
return 1;
}
int i = 0;
write(file,'\x00\x00',2); // ADDRESS
for (i = 0; i < NB_PAGES; i++) {
char buf[READ_SIZE] = {0};
if (read(file, buf, READ_SIZE) != READ_SIZE) {
printf("Failed reading\n");
close(file);
return 1;
}
dump_to_file(argv[1], buf, READ_SIZE);
}
close(file);
return 0;
}
We are testing serial port communication. There are two tty nodes /dev/ttyUSB8 and /dev/ttyUSB9 on the device.
When I transmit buffer from /dev/ttyUSB8 to /dev/ttyUSB9 I don't receive data on the /dev/ttyUSB9 read call if the buffer doesn't contain new line.
Transmit Code
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
void write_func()
{
int fdw;
int i;
fdw = open("/dev/ttyUSB8", O_RDWR);
printf("fdw : %d\n",fdw);
printf("%ld\n", write(fdw, "Hello", 6));
close(fdw);
}
int main()
{
int i;
write_func();
return 0;
}
Receive Code
void read_thread()
{
int fdr;
char buf[] = "NoData";
fdr = open("/dev/ttyUSB9", O_RDWR);
printf("fdr : %d\n",fdr);
printf("%s: %ld\n", __func__, read(fdr, buf, 6));
printf("%s\n", buf);
close(fdr);
}
int main()
{
int i;
read_thread();
return 0;
}
I don't receive data with the above call, but when I add '\n in the write call i get data in the read block call.
printf("%ld\n", write(fdw, "Hello\n", 7));
What is the significance of new line character in this..
Update:
I added the code to reset canonical mode, still it didn't work:
void write_thread()
{
int fdw;
int i;
struct termios config;
fdw = open("/dev/ttymib24", O_RDWR);
printf("fdw : %d\n",fdw);
tcgetattr(fdw, &config);
config.c_lflag &= ~ICANON;
tcsetattr(fdw, TCSANOW, &config);
printf("%ld\n", write(fdw, "Hello", 6));
close(fdw);
}
Your tty is probably in canonical mode.
Try to reset ICANON by using tcsetattr(). Something like this:
struct termios termiosv;
tcgetattr(fd, &termiosv);
termiosv.c_lflag &= ~ICANON;
tcsetattr(fd, TCSANOW, &termiosv);
More information in man page of termios:
In canonical mode:
* Input is made available line by line. An input line is available
when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at
the start of line). Except in the case of EOF, the line delimiter
is included in the buffer returned by read(2).
I am using this source code to read from the serial port of a linux machine. I am able to read from the port, but all of the values are in ascii gibberish ( i am reading the input from an xbox controller). I know I am sending it correctly, i.e. i can see on my side I am sending -128 - 127 as a char, but when I am converting it on my linux machine using atoi its returning 0, or when I try to cast the data to int it returns -48 , equivalent to 0 in ascii.
Is there a way for me to convert the incoming ascii into a readable integer like 64 or -114? I appreciate any help, thank you.
#include <stdlib.h>
#include <stdio.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
#include "rs232.h"
int main()
{
int i, n,
cport_nr=0, /* /dev/ttyS0 (COM1 on windows) */
bdrate=9600; /* 9600 baud */
unsigned char buf[4096];
char mode[]={'8','N','1',0};
if(RS232_OpenComport(cport_nr, bdrate, mode))
{
printf("Can not open comport\n");
return(0);
}
while(1)
{
n = RS232_PollComport(cport_nr, buf, 4095);
if(n > 0)
{
buf[n] = 0; /* always put a "null" at the end of a string! */
for(i=0; i < n; i++)
{
if(buf[i] < 32) /* replace unreadable control-codes by dots */
{
buf[i] = '.';
}
}
printf("received %i bytes: %s\n", n, (char *)buf);
}
#ifdef _WIN32
Sleep(100);
#else
usleep(100000); /* sleep for 100 milliSeconds */
#endif
}
return(0);
}
I'm actually implementing a very simple version of tail(1). In FreeBSD, I use kqueue to monitor a file for changes and then printing appended lines to the output. But this is not a portable way, as kqueue is only available in BSD family. Is there a general, efficient and platform-independent way to monitor files for changes in UNIX? I prefer not to use external libraries.
This is the code I've written:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
void die(const char*);
#define MAXLINE 1024
int main(int argc, char *argv[])
{
int fdes;
int kq;
int nev;
int flags;
off_t curoff;
char line[MAXLINE + 1];
ssize_t nbytes;
struct kevent change, event;
if (2 != argc)
{
fprintf(stderr, "Usage: %s path\n", argv[0]);
exit(EXIT_FAILURE);
}
if (-1 == (fdes = open(argv[1], O_RDONLY)))
die("open()");
if (-1 == (curoff = lseek(fdes, 0, SEEK_END)))
die("lseek()");
int ch = 0;
int i = 0;
while (i < 10)
{
read(fdes, &ch, 1);
if (ch == '\n')
i++;
if (10 > i)
lseek(fdes, --curoff, SEEK_SET);
}
if (-1 == (flags = fcntl(fdes, F_GETFL)))
die("fcntl()");
flags |= O_NONBLOCK;
if (-1 == fcntl(fdes, F_SETFL, flags))
die("fcntl()1");
while ((nbytes = read(fdes, line, MAXLINE)) > 0)
if (write(STDOUT_FILENO, line, nbytes) != nbytes)
die("write()");
if (-1 == (kq = kqueue()))
die("kqueue()");
EV_SET(&change, fdes, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
NOTE_EXTEND | NOTE_WRITE | NOTE_DELETE, 0, NULL);
if (-1 == kevent(kq, &change, 1, NULL, 0, NULL))
die("kevent()");
for (;;)
{
if (-1 == (nev = kevent(kq, NULL, 0, &event, 1, NULL)))
die("kevent()");
if (nev > 0)
{
if (event.fflags & NOTE_WRITE || event.fflags & NOTE_EXTEND)
{
while ((nbytes = read(fdes, line, MAXLINE)) > 0)
if (write(STDOUT_FILENO, line, nbytes) != nbytes)
die("write()");
}
else if (NOTE_DELETE & event.fflags)
{
printf("The file has been deleted\n");
break;
}
}
}
return 0;
}
void die(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
You can just keep doing the read() in a loop. If you read zero bytes,
check for an error. If there is no error, then you've hit EOF. At
EOF, stat() the filename, if it's gone, then the file was deleted. If
stat returns, compare the st_dev and st_ino fields of the stat to
the results from fstat (cache this when you open the file). If they're
different, the path was deleted and re-created. Sleep as long as you
care to after the delete check before trying another read.
I'm studying UNIX programming and was experimenting with read/write system calls.
I have a file with a pair of integer:
4 5
and I wrote this code to read the numbers:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
typedef struct prova {
int first;
int second;
} prova_t;
int main(void) {
int fd;
prova_t origin;
prova_t result;
ssize_t bytes_read;
size_t nbytes;
fd = open("file.bin", O_WRONLY | O_CREAT);
origin.first = 24;
origin.second = 3;
write(fd, &origin, sizeof(prova_t));
close(fd);
fd = open("file.bin", O_RDONLY);
nbytes = sizeof(prova_t);
/* 1.BAD */
bytes_read = read(fd, &result, nbytes);
write(STDOUT_FILENO, &(result.first), sizeof(int));
write(STDOUT_FILENO, &(result.second), sizeof(int));
close(fd);
/* 2.GOOD */
nbytes = sizeof(int);
bytes_read = read(fd, &(result.first), nbytes);
write(STDOUT_FILENO, &(result.first), bytes_read);
bytes_read = read(fd, &(result.second), nbytes);
write(STDOUT_FILENO, &(result.second), bytes_read);
return 0;
}
In my first attempt I tried to read the whole struct from file and write its members to stdout. In this way, along with the numbers, I get some weird characters
4 5
E�^�
In my second attempt I read the numbers one by one and there were no problems in the output.
Is there any way to read and write the struct using the first method?
Edit: I updated the code to reflect suggestion from other users but still getting strange characters instead of numbers
First, let's do a hex dump to see what is really stored in the file.
hexdump -C b.txt or od -t x2 -t c b.txt are two examples (od is for octal dump, more common, less pretty output in my opinion)
00000000 34 20 35 0a |4 5.|
00000004
That's is what the file looks like if it was a created as an ASCII text file (such as using a text editor like vi). You can use man ascii to double check the hexadecimal values.
Now if you had a binary file that only contains two 8-bit bytes, in the system's native byte ordering (e.g. little-endian for x86, big endian for MIPS, PA-RISC, 680x0) then the hexdump would look like:
00000000 04 05 |..|
00000004
Here is the code to both create (open & write) a binary file, and read it back.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h> /* uint32_t */
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
/* User has read & write perms, group and others have read permission */
const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
typedef struct prova {
uint32_t first;
uint32_t second;
} prova_t;
#define FILENAME "file.b"
/* 'Safe' write */
int safewrite( int fd, const void *p, size_t want) {
int ret;
errno = 0;
while (want) {
ret = write(fd, (uint8_t *)p, want);
if (ret <= 0) {
if (errno != EINTR && errno != EAGAIN) {
return -1;
}
errno = 0;
continue;
}
want -= ret;
p = (uint8_t*) p + ret;
}
return 0;
}
int saferead(int fd, const void *p, size_t want) {
int ret;
errno = 0;
while (want) {
ret = read(fd, (uint8_t*)p, want);
if( ret == 0 )
return -1; /* EOF */
if (ret <= 0) {
if( errno != EINTR && errno != EAGAIN ) {
return -1;
}
errno = 0;
continue;
}
want -= ret;
p = (uint8_t*) p + ret;
}
return 0;
}
int main(int argc, char **argv) {
int fd;
prova_t result;
size_t nbytes;
/* Create file */
fd = creat(FILENAME, mode);
if (fd < 0) {
fprintf(stderr, "Unable to open " FILENAME ": %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
nbytes = sizeof(prova_t);
result.first = 4;
result.second = 5;
if (0 != safewrite(fd, &result, nbytes)) {
fprintf(stderr, "Unable to write to " FILENAME ": %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
close(fd);
fd = -1;
/* Reopen and read from binary file */
fd = open(FILENAME, O_RDONLY);
nbytes = sizeof(prova_t);
if (0 != saferead(fd, &result, nbytes)) {
fprintf(stderr, "Unable to read file \"" FILENAME "\": %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
close(fd);
printf( "Read: %d %d (%#.02x%.02x)\n",
result.first, result.second,
result.first, result.second);
return EXIT_SUCCESS;
}
Now the data file contents look like:
00000000 04 00 00 00 05 00 00 00 |........|
00000008
Because the integers were specified as 32-bit integers (32-bits / 8 bits per byte = 4 bytes). I'm using a 64-bit system (little endian, x86), so I wanted to be explicit so the your results should match, assuming little-endian.
You tried to read to a struct containing two ints, by passing a pointer to some data and telling read that you had one int's worth of storage. The first should be
bytes_read = read(fd, &result, sizeof(prova_t));
From the name of your file, I assume that you are trying to read a text file. read from unistd.h reads from binary files. If you are indeed trying to read from a text file, you should use fscanf or in ifstream
To read a struct in binary:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
typedef struct prova {
int first;
int second;
} prova_t;
int main(void) {
int fd;
prova_t result;
ssize_t bytes_read;
size_t nbytes;
prova_t initial;
// create a binary file
fd = open("file.bin", O_WRONLY | O_CREAT);
initial.first = 4;
initial.second = 5;
write(fd, &initial, sizeof(prova_t));
close(fp);
// read it back
fd = open("file.bin", O_RDONLY);
nbytes = sizeof(prova_t);
bytes_read = read(fd, &result, nbytes);
write(STDOUT_FILENO, &(result.first), sizeof(int));
write(STDOUT_FILENO, &(result.second), sizeof(int));
close(fp);
return 0;
}
Include flatbuffers/util.h, there are save and load functions sepeartely
SaveFile(const char *name, const char *buf, size_t len,
bool binary);
LoadFile(const char *name, bool binary, std::string *buf);