无法在 C 代码中模仿 `echo` 命令的动作
Cannot mimic the action of `echo` command in C code
我写了几个 bash 脚本文件,它们通过两个串行端口进行通信。可以将一个脚本视为接收器,将另一个脚本视为发送器。接收脚本逐行读取并显示数据,直到读取到字符序列<break>
。然后它停止监听,并将 one
或 two
的字符序列发送回发送器。因此脚本是:
#!/bin/bash
# Read and reply bash script.
exec 3<> /dev/ttyS4
while true; do
#cat -v /dev/ttyS4 | while read -r input; do
while read -r -u 3 input; do
if [ "$input" = "<break>" ]
then
echo "break command received."
break
else
echo -e "${input}"
fi
done
echo "Sending selection"
if [ "$selection" = "one" ]
then
selection="two"
else
selection="one"
fi
echo "$selection" >&3
done
发送端脚本发送一些字符数据,然后等待上面接收端脚本one
或two
的回复:
exec 4<> /dev/ttyS0
selection="one"
while true; do
echo "************************************" > /dev/ttyS0
echo " Selection: ${selection}" > /dev/ttyS0
echo "************************************" > /dev/ttyS0
echo "<break>" > /dev/ttyS0
read -r -t 3 -u 4 input
if [ -z "$input" ]
then
echo "Response from remote timed out."
elif [ "$input" = "one" ]
then
selection=$input
elif [ "$input" = "two" ]
then
selection=$input
colour=$RED
else
echo "Unknown selection: $input"
fi
sleep 1
done
以上两个脚本工作正常,接收脚本正确识别 <break>
字符序列。
我想用一个小的 C 程序替换 'transmitter' 脚本,但是我发现当我发送字符序列 <break>
时,接收器脚本这次 NOT 标识为 'end-of-transmission',是对标准输出的简单回显 <break>
,而不是 break command received
。我已经尝试了几种方法,例如添加转义字符,但是 Bash echo
的工作方式以及我在 C 代码中发送数据的方式显然有所不同:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#define TRUE 1
#define FALSE 0
int main(int argc, char *argv[]) {
int selected_tool = DEFAULT_TOOL_SELECTION;
FILE *ser2_fd_write, *ser2_fdrw;
struct termios tios, tios_w; // ser 2 termios structure
int ser2_fd, ser2_fdw;
char read_buffer[BUFFER_SIZE];
struct timespec tdelay;
bzero(&tdelay, sizeof(tdelay));
tdelay.tv_sec = 1;
tdelay.tv_nsec = 5000;
if ((ser2_fd = open("/dev/ttyS0", O_RDWR)) == -1){
printf("Unable to open ttyS0 as read-write only.\n");
return EXIT_FAILURE;
}
bzero(&tios, sizeof(tios));
cfsetispeed(&tios, B38400);
cfsetospeed(&tios, B38400);
tios.c_cflag = B38400 | CS8 | CLOCAL | CREAD | CRTSCTS;
tios.c_iflag = IGNPAR;
tios.c_oflag = 0;
tios.c_lflag = ICANON; //0
tios.c_cc[VTIME] = 0;
tios.c_cc[VMIN] = 10;
tcflush(ser2_fd, TCIFLUSH);
if (tcsetattr(ser2_fd, TCSANOW, &tios) == -1){
printf("Could not set ser 2 attributes.\n");
return -1;
}
if ((ser2_fdrw = fdopen(ser2_fd, "awr")) == NULL){
printf("Unable to open file descriptor.\n");
return EXIT_FAILURE;
}
while(1){
push_to_ser2(ser2_fdrw, selected_tool);
/*
* This is where sending <break> differs from echo
*/
//fputs("break", ser2_fdrw);
fprintf(ser2_fdrw, "break\r\n");
//fprintf(ser2_fdrw, "\r\n");
//write(ser2_fd,"break\r\n", 9);
fflush(ser2_fdrw);
int c = 0;
nanosleep(&tdelay, NULL);
tcflush(ser2_fd, TCIOFLUSH);
tcdrain(ser2_fd);
fcntl(ser2_fd, F_SETFL, 0);
if ( (c = read(ser2_fd, read_buffer, BUFFER_SIZE)) > 0){
read_buffer[c] = 0;
if (strcmp(read_buffer, "one\r\n") == 0){
selected_tool = 1;
} else if (strcmp(read_buffer, "two\r\n") == 0){
selected_tool = 2;
}
}else{
}
}
return EXIT_SUCCESS;
}
/*
* Convenience function to push data to ser 2
* *c_data pointer to the card data
* *t_data pointer to the tool data
*/
void push_to_ser2(FILE * fd, int tool){
fprintf(fd, "**********************************************************\n");
fprintf(fd, "* *\n");
fprintf(fd, "* Tool %d Data *\n", tool);
fprintf(fd, "* *\n");
fprintf(fd, "**********************************************************\n");
fprintf(fd,"\r\n");
fflush(fd);
}
我也尝试过对 termios
结构进行各种更改,但没有任何区别。有什么想法吗?
看起来好像 bash 脚本正在发送文字字符串 <break>
,包括尖括号。同时,C 程序只发送 break
。将尖括号添加到 C 程序中的文字。 (这些括号对 echo
或 fprintf
没有任何特殊意义,因此它们按原样发送。)
您的代码有几个问题需要更正:
- 而不是
bzero(&tios, sizeof(tios))
代码应该调用 tcgetattr() 来正确初始化结构。对于规范输入来说,这可能是一个严重的问题,因为现有代码会将所有控制代码规范归零,而不是具有正确的定义。
- 代码应该执行按位运算而不是直接赋值(以保留现有设置)。参见 Setting Terminal Modes Properly。
- 您在指定非规范输出和规范输入,这是一种非典型组合。似乎你想要输入和输出的规范模式。
- VMIN 和 VTIME 仅对非规范输入有意义,不应为规范输入指定(因为它破坏了 VEOF 和 VEOL 字符规范)。
read(ser2_fd,...)
正在静默忽略错误。
- 不使用 fdopen() 和 fprintf(),只需使用 write(ser2_fd , ...) 来简化代码和开销。缺点是您必须指定字节数并使用 sprintf() 执行整数到字符串的转换。
我写了几个 bash 脚本文件,它们通过两个串行端口进行通信。可以将一个脚本视为接收器,将另一个脚本视为发送器。接收脚本逐行读取并显示数据,直到读取到字符序列<break>
。然后它停止监听,并将 one
或 two
的字符序列发送回发送器。因此脚本是:
#!/bin/bash
# Read and reply bash script.
exec 3<> /dev/ttyS4
while true; do
#cat -v /dev/ttyS4 | while read -r input; do
while read -r -u 3 input; do
if [ "$input" = "<break>" ]
then
echo "break command received."
break
else
echo -e "${input}"
fi
done
echo "Sending selection"
if [ "$selection" = "one" ]
then
selection="two"
else
selection="one"
fi
echo "$selection" >&3
done
发送端脚本发送一些字符数据,然后等待上面接收端脚本one
或two
的回复:
exec 4<> /dev/ttyS0
selection="one"
while true; do
echo "************************************" > /dev/ttyS0
echo " Selection: ${selection}" > /dev/ttyS0
echo "************************************" > /dev/ttyS0
echo "<break>" > /dev/ttyS0
read -r -t 3 -u 4 input
if [ -z "$input" ]
then
echo "Response from remote timed out."
elif [ "$input" = "one" ]
then
selection=$input
elif [ "$input" = "two" ]
then
selection=$input
colour=$RED
else
echo "Unknown selection: $input"
fi
sleep 1
done
以上两个脚本工作正常,接收脚本正确识别 <break>
字符序列。
我想用一个小的 C 程序替换 'transmitter' 脚本,但是我发现当我发送字符序列 <break>
时,接收器脚本这次 NOT 标识为 'end-of-transmission',是对标准输出的简单回显 <break>
,而不是 break command received
。我已经尝试了几种方法,例如添加转义字符,但是 Bash echo
的工作方式以及我在 C 代码中发送数据的方式显然有所不同:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#define TRUE 1
#define FALSE 0
int main(int argc, char *argv[]) {
int selected_tool = DEFAULT_TOOL_SELECTION;
FILE *ser2_fd_write, *ser2_fdrw;
struct termios tios, tios_w; // ser 2 termios structure
int ser2_fd, ser2_fdw;
char read_buffer[BUFFER_SIZE];
struct timespec tdelay;
bzero(&tdelay, sizeof(tdelay));
tdelay.tv_sec = 1;
tdelay.tv_nsec = 5000;
if ((ser2_fd = open("/dev/ttyS0", O_RDWR)) == -1){
printf("Unable to open ttyS0 as read-write only.\n");
return EXIT_FAILURE;
}
bzero(&tios, sizeof(tios));
cfsetispeed(&tios, B38400);
cfsetospeed(&tios, B38400);
tios.c_cflag = B38400 | CS8 | CLOCAL | CREAD | CRTSCTS;
tios.c_iflag = IGNPAR;
tios.c_oflag = 0;
tios.c_lflag = ICANON; //0
tios.c_cc[VTIME] = 0;
tios.c_cc[VMIN] = 10;
tcflush(ser2_fd, TCIFLUSH);
if (tcsetattr(ser2_fd, TCSANOW, &tios) == -1){
printf("Could not set ser 2 attributes.\n");
return -1;
}
if ((ser2_fdrw = fdopen(ser2_fd, "awr")) == NULL){
printf("Unable to open file descriptor.\n");
return EXIT_FAILURE;
}
while(1){
push_to_ser2(ser2_fdrw, selected_tool);
/*
* This is where sending <break> differs from echo
*/
//fputs("break", ser2_fdrw);
fprintf(ser2_fdrw, "break\r\n");
//fprintf(ser2_fdrw, "\r\n");
//write(ser2_fd,"break\r\n", 9);
fflush(ser2_fdrw);
int c = 0;
nanosleep(&tdelay, NULL);
tcflush(ser2_fd, TCIOFLUSH);
tcdrain(ser2_fd);
fcntl(ser2_fd, F_SETFL, 0);
if ( (c = read(ser2_fd, read_buffer, BUFFER_SIZE)) > 0){
read_buffer[c] = 0;
if (strcmp(read_buffer, "one\r\n") == 0){
selected_tool = 1;
} else if (strcmp(read_buffer, "two\r\n") == 0){
selected_tool = 2;
}
}else{
}
}
return EXIT_SUCCESS;
}
/*
* Convenience function to push data to ser 2
* *c_data pointer to the card data
* *t_data pointer to the tool data
*/
void push_to_ser2(FILE * fd, int tool){
fprintf(fd, "**********************************************************\n");
fprintf(fd, "* *\n");
fprintf(fd, "* Tool %d Data *\n", tool);
fprintf(fd, "* *\n");
fprintf(fd, "**********************************************************\n");
fprintf(fd,"\r\n");
fflush(fd);
}
我也尝试过对 termios
结构进行各种更改,但没有任何区别。有什么想法吗?
看起来好像 bash 脚本正在发送文字字符串 <break>
,包括尖括号。同时,C 程序只发送 break
。将尖括号添加到 C 程序中的文字。 (这些括号对 echo
或 fprintf
没有任何特殊意义,因此它们按原样发送。)
您的代码有几个问题需要更正:
- 而不是
bzero(&tios, sizeof(tios))
代码应该调用 tcgetattr() 来正确初始化结构。对于规范输入来说,这可能是一个严重的问题,因为现有代码会将所有控制代码规范归零,而不是具有正确的定义。 - 代码应该执行按位运算而不是直接赋值(以保留现有设置)。参见 Setting Terminal Modes Properly。
- 您在指定非规范输出和规范输入,这是一种非典型组合。似乎你想要输入和输出的规范模式。
- VMIN 和 VTIME 仅对非规范输入有意义,不应为规范输入指定(因为它破坏了 VEOF 和 VEOL 字符规范)。
read(ser2_fd,...)
正在静默忽略错误。- 不使用 fdopen() 和 fprintf(),只需使用 write(ser2_fd , ...) 来简化代码和开销。缺点是您必须指定字节数并使用 sprintf() 执行整数到字符串的转换。