I hope this question hasn't been asked before, I have had a check and stumbled across this but it doesn't really help for the automated testing that I am trying to do.
I want to create a simple testing script that will be able to create a serial device, allow me to "echo 'w' > device" and get a value X back from it.
Manually using socat to create multiple points doesn't really help as my code works with viewing just one port, not a slave/master style one.
I've included my function which interacts with a serial port, and works with actual physical hardware!
int interactWithPort(char* portID, char BW, char* offOn){
speed_t baud = B9600; // baud rate
int fd = open(portID, (BW == 'B') ? O_RDWR : O_WRONLY); //Open the port with the correct RW settings
struct termios settings; // structure for the settings that will be used for the port
tcgetattr(fd, &settings);
cfsetospeed(&settings, baud); // baud rate
settings.c_cflag &= ~PARENB; // no parity
settings.c_cflag &= ~CSTOPB; // 1 stop bit
settings.c_cflag &= ~CSIZE;
settings.c_cflag |= CS8 | CLOCAL; // 8 bits
settings.c_lflag = ICANON; // canonical mode
settings.c_oflag &= ~OPOST; // raw output
tcsetattr(fd, TCSANOW, &settings); // apply the settings
tcflush(fd, TCOFLUSH);
fcntl(fd, F_SETFL, 0); // apply file control operations
// Initialize file descriptor sets
fd_set read_fds, write_fds, except_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&except_fds);
FD_SET(fd, &read_fds);
// Set timeout to 1.0 seconds
struct timeval timeout;
timeout.tv_sec = 3; // Read timeout of around 3 seconds, any more than this and something has went wrong
timeout.tv_usec = 1;
int w = 0;
if(BW == 'W'){
w = (int) write(fd, offOn, 1);
}else{
w = (int) write(fd, "w", 1); // writes to the port a w or a 1/0 depending on function
}
//If theres an error in writing to the scales then tell us!
if(w < 0)
fprintf(stderr, "Error writting to device: %s\n", portID);
//If we flip switch to water then return as it's worked
if(BW == 'W') return w;
// Wait for input to become ready or until the time out; the first parameter is
// 1 more than the largest file descriptor in any of the sets
if (select(fd + 1, &read_fds, &write_fds, &except_fds, &timeout) == 1) {
//This buffer holds the data from the serial port
char buffer[32]; //Could reduce this to around 18 in length but better to have more buffering room
// fd is ready for reading
u_short n = (u_short) read(fd, buffer, sizeof(buffer)); //Reads the length of the serial data
buffer[n] = 0;
char * result = deblank(buffer);
close(fd); // close the connection
return atoi(result); // convert the result to a number and cast to be short
}
else
fprintf(stderr, "Timeout error\n");
return 0; // timeout or error
}
I have played around with some python scripting but it still has the same problem of slave/master and can't read from the desired address, but can write to it no problem.
I have played around with something like this python script but it still has the slave/master setup that doesn't work for me:
import os
import pty
import serial
import time
master, slave = pty.openpty()
s_name = os.ttyname(slave)
ser = serial.Serial(s_name, baudrate=9600)
print(os.ttyname(slave))
print(os.ttyname(master))
# Create a symbolic link for the C program to use!
src = s_name
dst = "/home/nathan/test"
# This creates a symbolic link on python in tmp directory
os.symlink(src, dst)
# To Write to the device to get a reading
# ser.write('w'.encode('ascii'))
# To read from the device
while(True):
if "w" in str(os.read(master, 1000)):
ser.write("100".encode('ascii'))
print("worked")
break
time.sleep(1)
print(os.read(slave, 1000))
# Clean up by deleting the temp link that we used
os.unlink(dst)
... it still has the same problem of slave/master and can't read from the desired address, but can write to it no problem.
Your code has missing salient initialization.
The input baudrate should be specified:
cfsetispeed(&settings, baud);
The CREAD flag is required to enable the receiver:
settings.c_cflag |= CREAD;
It's also common to specify the serial port as a non-controlling terminal:
fd = open(portID, O_RDWR | O_NOCTTY);
For consistent logic (i.e. match your other tests), the conditional should be
fd = open(portID, (BW == 'W' ? : O_WRONLY : O_RDWR) | O_NOCTTY);
Between the write() and select(), a tcdrain operation (to wait until all output written has been transmitted) would be appropriate:
tcdrain(fd);
The big picture isn't obvious, but appears that you're opening, initializing, and closing the serial port just to write and read once; i.e. all that is in just one procedure.
This should be typically be separated into at least three sections of a program for modularity.
You seem to use blocking canonical mode, but output only one byte (i.e. where's the line terminator?).
Overall your code fails to check the return codes from syscalls for error conditions. Since your program does not behave as expected, you could be ignoring salient information.