Доброго времени суток.Прошу сильно не пинать, если не туда написал - мой первый пост.
Искал готовую программку для работы с Com-портом, для отслеживания замыкания 2 и 3 ноги на нем и по этому событию отработки определенного скрипта. Готового не нашел, поэтому взял с соседнего поста программку для работы с Com-портом и немного переделал под себя. Вот делюсь, вдруг кто-то будет подобное искать...Программу нужно запускать с правами root, т.к. доступ в Com-порту на запись и чтение по умолчанию имеет только он, либо назначать соответствующие права на файл /dev/ttyS0. Программа запускается, открывает порт, пишет в порт и слушает. Как только замыкаются 2 и 3 нога порта на время более 1 секунды, запускает скрипт, ждет 30 секунд. Есть проблемное место, для меня не критичное, но я на всякий случай предупреждаю о нем: при замыкании контактов программа запускает скрипт дважды, сразу после замыкания контактов и еще раз через 30 секунд. Затем выходит опять в ждущий режим. Но так для меня это не критично, оставил как есть. Для отладки выводит служебную информацию в консоль, которую можно убрать.
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#define PORT "/dev/ttyS0" //Здесь указывается с каким портом будем работать
#define BAUDRATE B9600 //Скорость работы по порту
int main(){
//int fd = open(PORT, O_RDWR | O_NOCTTY); //"Работа порта с ожиданием прихода данных
// close(fd);
int fd = open(PORT, O_RDWR | O_NDELAY ); //Именно так работает без ожидания com порта на чтение
if (fd<0)
return 1;
if (!isatty(fd))
return 2;
struct termios port_cfg;
if (tcgetattr(fd, &port_cfg)<0)
return 3;
cfmakeraw(&port_cfg);
cfsetospeed(&port_cfg, BAUDRATE);
cfsetispeed(&port_cfg, BAUDRATE);
if (tcsetattr(fd, TCSANOW, &port_cfg)<0)
return 4;
unsigned int val,val2, a, ret; //инициализация переменных
val=11111; //задание значения переменных - эти данные будут посылаться в порт
a=0;//задание значения переменных
while (a>=0) //начало цикла
{
ret = write(fd, &val, 1); // пишем в порт
printf ("ret=%d \n",ret);
printf ("val=%d \n",val);
if (ret!=1) return 5;
a++,//увеличение значения на 1
printf ("a=%d \n\n",a);
//ret2=0;
//printf ("До чтения ret2=%d \n",ret2);
int ret2 = read(fd, &val2,1); //пытаемся прочитать из порта
printf ("После чтения ret2=%d \n\n",ret2);
if (ret2==1) //Если произошло чтение, то
{
system ("/home/user/runremote.sh");
printf ("Сработало\n\n");
usleep(30000000); //30 секунд}
usleep(1000000); // 1 секунда
}
return 0;
close(fd);}
Есть ли в ней серьезные ошибки? Вроде работает, правда я не программист...
>немного топорно, но сойдет
пара моментов:ты пишешь и читаешь 1 байт - зачем тебе переменные типа int да еще 11111 ?
в компортовой системе есть буферы , где могут накапливаться твои записанные байты, вызывая ложное срабатывание 2й раз . Посему если считал 1 байт - хорошо было б почитать еще, пока не вернет 0 , т.е. выгрести все что есть .
> return 0;
> close(fd);
> Есть ли в ней серьезные ошибки? Вроде работает, правда я не программист...вот эти две строки местами поменяй :)
> Есть ли в ней серьезные ошибки?Не уверен насчет серьезности, но если вам нужно сделать code review, то см. ниже
> #define PORT "/dev/ttyS0" //Здесь указывается с каким портом будем работать
> #define BAUDRATE B9600 //Скорость работы по порту
> system ("/home/user/runremote.sh");Я бы сделал передачу этих значений через командную строку. Это можно легко сделать с помощью getopt(). См. man 2 getopt. Я делал так:
https://gitorious.org/send-arp/send-arp/source/49789e4b75050...
> int main(){
Нестандартная сигнатура main(), лучше main(void) сделать, или main(int argc, char *argv[]), если будут обрабатываться параметры командной строки (через getopt() например).
> while (a >= 0)у вас "a" -- это unsigned int, так что "a" никогда не будет меньше нуля. Если нужен вечный цикл -- пишите просто while (1) или for (;;)
> unsigned int val,val2, a, ret; //инициализация переменных
1. область видимости ret можно уменьшить, т.е. внести ret в цикл
2. это не инициализация, это их объявление (я насчет комментария); комментарии (и строки, которые выводятся на экран тоже) лучше писать на английском языке, кстати: тогда программа будет ASCII файлом, не будет никогда проблем с кодировками, и самое главное -- вашу программу смогут читать все люди на Земле :)> ret = write(fd, &val, 1); // пишем в порт
> int ret2 = read(fd, &val2,1); //пытаемся прочитать из портасистемные вызовы read() и write() возвращают ssize_t, т.е. ret и ret2 должны быть signed. Т.е. уберите unsigned в их декларации.
> if (!isatty(fd))
> return 2;и все остальные return: перед этими return нужно закрывать файловый дескриптор fd.
Также перед тем, как выйти из программы по ошибке, неплохо бы писать пользователю (в д.с. самому себе), какая ошибка произошла. Писать надо в поток вывода ошибок: fprintf(stderr, ...). Или использовать perror(), если тот вызов при ошибке устанавливает значение errno (надо смотреть маны).
Ну и еще номера ошибок лучше сделать константами, чтобы не было magic numbers.
> val=11111; //задание значения переменных - эти данные будут посылаться в порт
лучше сделать это константой (например с помощью #define), иначе получается magic number.
> a++,//увеличение значения на 1
> printf ("a=%d \n\n",a);я бы сделал "a++;"
и да, лучше не ставить таких комментариев, которые дублируют код, они только мешают> printf ("ret=%d \n",ret);
> printf ("val=%d \n",val);
> printf ("a=%d \n\n",a);Здесь выводятся беззнаковые переменные, но используется %d. Для беззнаковых надо использовать %u. Или как вариант -- эти переменные не должны быть беззнаковые.
> ret = write(fd, &val, 1); // пишем в порт
> if (ret!=1) return 5;в случае ошибки write() всегда вернет -1, лучше на него и проверять (а то измените когда-нибудь кол-во отправляемых байт, и уже не будет работать); и еще перед выходом по этой ошибке лучше через perror() выводить строку ошибки. Тоже самое для всех остальных системных вызовов. Читайте "man 2 write", "man 2 read" и т.д.
> usleep(30000000); //30 секунд
> usleep(1000000); // 1 секундаИз man 3 usleep:
POSIX.1-2001 declares this function obsolete; use nanosleep(2) instead> return 0;
> close(fd);Наоборот.
> return 0;
лучше вместо 0 использовать EXIT_SUCCESS из stdlib.h.
Ну что еще можно улучшить -- разбить на функции, чтобы не всё было в main(). Стиль кодирования взять нормальный, например этот: https://www.kernel.org/doc/Documentation/CodingStyle . Неплохо бы ловить и обрабатывать Ctrl+C (SIGINT). А вообще мне кажется что всё это можно было сделать на bash, через stty/echo/cat, или поискать готовую тулзовину. Хотя как пример работы с serial console полезно, только код тогда нужно подрихтовать.
И еще маленький совет: заведите себе аккаунт на gitorious.org или github.com и залейте проект туда. Добавьте Makefile и README для него. Может потихоньку улучшите его. Так будет удобней и людям показывать, и сами сможете из любой точки земли его посмотреть/скачать, и при устройстве на работу можно показывать. Может кто-то коммитами поможет и т.д., короче одни выгоды :)
> Есть ли в ней серьезные ошибки?Всё выкинуть, в порт ничё писать не нужно, активность ловить через poll/select.
>> Есть ли в ней серьезные ошибки?
> Всё выкинуть, в порт ничё писать не нужно, активность ловить через poll/select.какая там будет активность, если он замкнул тхд с рхд ?
>>> Есть ли в ней серьезные ошибки?
>> Всё выкинуть, в порт ничё писать не нужно, активность ловить через poll/select.
> какая там будет активность, если он замкнул тхд с рхд ?Создать :)