Raspberry и бесконечный цикл Serial

Тема в разделе "Raspberry Pi", создана пользователем ut2k5, 1 фев 2016.

  1. ut2k5

    ut2k5 Нерд

    Добрый день!
    Написал программу, которая запускается автоматически
    Смысл программы в бесконечном цикле постоянно слушать по usb (serial) приходящие пакеты от подключенной arduino
    когда приходит правильная команда, запускается определенно видео
    проблема в следующем, если отключить малину от питания, то слетает графический режим, и не возможно запустить в дальнейшем startx, т.е. приходится заново переустанавливать систему на малине
    подозреваю, что работа в linux с com портами происходит как и с файлами, вот код программы:
    Код (C++):
    #include <stdlib.h>
    #include <pty.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <errno.h>
    #include <termios.h>
    #include <string.h>

    #define STATIONS_MAX_NUM 5   // Количество станций в нашем плейлисте

    // Это наш маленький типа плэйлист
    // Между кавычек вставляем URL потока вещания станци
    char *station1[]={"./Videos/Movie_01.mov", NULL};
    char *station2[]={"./Videos/Movie_02.mov", NULL};
    char *station3[]={"./Videos/Movie_03.mov", NULL};
    char *station4[]={"./Videos/Movie_04.mov", NULL};
    char *station5[]={"./Videos/Movie_05.mov", NULL};

    char* getDeviceFileName(int argc, char **argv)
    {
       if (argc < 2) {
          fprintf(stderr, "Argument missing: Arduino device file.\n");
          exit(EXIT_FAILURE);
       }
       return argv[1];
    }

    int openDeviceFile(const char* fileName, const char* mode)
    {
       FILE* file;

       file = fopen(fileName, mode);
       if (file == NULL) {
          perror(fileName);
          exit(EXIT_FAILURE);
       }
       return fileno(file);
    }

    unsigned int getBaudRate(int argc, char **argv)
    {
       return (argc > 2) ? atoi(argv[2]) : 57600;
    }

    void setAttr(int fd, unsigned int baud)
    {
       struct termios toptions;
       speed_t brate;

       if (tcgetattr(fd, &toptions) < 0) {
          perror("Can't get term attributes");
          exit(EXIT_FAILURE);
       }

       switch(baud) {
          case 4800:   brate=B4800;   break;
          case 9600:   brate=B9600;   break;
        #ifdef B14400
          case 14400:  brate=B14400;  break;
        #endif
          case 19200:  brate=B19200;  break;
        #ifdef B28800
          case 28800:  brate=B28800;  break;
        #endif
          case 38400:  brate=B38400;  break;
          case 57600:  brate=B57600;  break;
          case 115200: brate=B115200; break;
          case 230400: brate=B230400; break;
          default:
             fprintf(stderr, "Invalid baud rate value: %d\n", baud);
             exit(EXIT_FAILURE);
       }
       cfsetispeed(&toptions, brate);
       cfsetospeed(&toptions, brate);

       // 8N1
       toptions.c_cflag &= ~PARENB;
       toptions.c_cflag &= ~CSTOPB;
       toptions.c_cflag &= ~CSIZE;
       toptions.c_cflag |= CS8;
       // no flow control
       toptions.c_cflag &= ~CRTSCTS;

       toptions.c_cflag |= CREAD | CLOCAL;  // turn on READ & ignore ctrl lines
       toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl

       toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
       toptions.c_oflag &= ~OPOST; // make raw

       // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
       toptions.c_cc[VMIN]  = 0;
       toptions.c_cc[VTIME] = 20;

       if (tcsetattr(fd, TCSANOW, &toptions) < 0) {
          perror("Can't set term attributes");
          exit(EXIT_FAILURE);
       }
    }

    void sendCommand(int fd, const char *cmd)
    {
       write(fd, cmd, strlen(cmd));
    }

    void displayResult(int fd)
    {
      char buf[256];
      ssize_t sz;

      do {
        sz = read(fd, buf, 255);
        buf[sz] = 0;
        printf("%s", buf);
      }
      while (sz);
    }

    void do_child (char **omx_arg)
    {
      if (execlp("omxplayer", "omxplayer", *omx_arg) < 0)
      {
        perror("exec omxplayer");
        exit(1);
      }
    }


    int do_parent (int fd, pid_t p, int n, int fdserial)
    {
        char buf[256];
        ssize_t sz;
        int num;
        do {
        sz = read(fdserial, buf, 255);
        //buf[sz] = 0;

        //printf("%s", buf);
            if (strncmp("play_movie_0", buf, 12) == 0){if(n!=1){n=1;break;}}
        if (strncmp("play_movie_1", buf, 12) == 0){if(n!=2){n=2;break;}}
        if (strncmp("play_movie_2", buf, 12) == 0){if(n!=3){n=3;break;}}
        if (strncmp("play_movie_3", buf, 12) == 0){if(n!=4){n=4;break;}}
        if (strncmp("play_movie_4", buf, 12) == 0){if(n!=5){n=5;break;}}
        printf("%s - %d", buf, strlen(buf));
        } while (1);

        bcm2835_delay(500);                          // Эта задержка в 500мс нужна для фиксации нажатия кнопки

        char r;
        write(fd, "q", 1);                             // Здесь мы посылаем omxplayer-у эквивалент нажатия на клавишу "q" клавиатуры
                                                       // что заставляет плеер завершить работу

        while (read(fd, &r, 1) > 0) { write(1, &r, 1); }       // Всё, что ниже, нужно для корректного завершения процесса
        waitpid(p, 0, 0);
        close(fd);                                        // Всё, плеер выключен, процесс завершён. Можно начинать всё заново с новой станцией
        //++n;                                    // Увеличиваем порядковый номер проигрываемой станции на 1
        if (n>STATIONS_MAX_NUM) n=1;         // Проверяем, если мы проиграли все станции в плей-листе, то устанавливаем номер снова на 1
        return(n);                                        // Выходим из функции и возвращаем номер очередной станции
    }

    // Функция проигрывания очередной станции
    // Входной параметр n- порядковый номер проигрываемой станции.
    // Если добавили в плей-лист ещё станций, то и здесь нужно увеличить
    // количество строк case

    void play_station(int n)
    {
            switch (n)
            {
            case 1: do_child(station1);
                    case 2: do_child(station2);
                    case 3: do_child(station3);
                    case 4: do_child(station4);
            case 5: do_child(station5);
            default: ;
            }
    }


    // Основная программа
    int main()
    {

                int fd;              // файловый дескриптор
                int n=4;            // порядковый номер станции

            const char fileName[] = "/dev/serial/by-id/usb-Arduino_Srl_Arduino_Uno_75435353038351217041-if00";
            const char fileMode[] = "r+";
            unsigned int baud = 115200;
            int fdserial;

            //fileName = getDeviceFileName(argc, argv);
            fdserial = openDeviceFile(fileName, fileMode);
            //baud = getBaudRate(argc, argv);
            setAttr(fdserial, baud);
            //displayResult(fdserial);

                while(n)           // Бесконечный цикл. Выход не предусмотрен.
                {
                    pid_t p = forkpty(&fd, 0, 0, 0); // Запускаем параллельный процесс в псевдотерминале
                    switch (p)
                    {
                         case 0:  play_station(n);          // Если всё получилось, запускаем плеер в созданном процессе
                         case -1: perror("forkpty");      // Иначе аварийное завершение.
                         exit(EXIT_FAILURE);

                         default: break;
                    }
                    n=do_parent(fd, p, n, fdserial); // А в основном процессе начинаем следить за кнопкой.
                }
            close(fdserial);
    }
     
     
  2. embed

    embed Нерд

    А какая разница, как работает с сериалом linux? Вообще-то там других сущностей для работы, кроме файлов, и нету.
    Сделайте автозапуск графической оболочки при помощи raspi-config, а программку вашу запустите в теринале уже внутри оболочки. Ну или извне, но тогда плееру придется указывать дисплей на котором надо запустится.
    Да и вообще это все проще написать на питоне, получится десяток строк вместо огромной портянки, и компилировать не надо...
     
  3. ut2k5

    ut2k5 Нерд

    нет, система ложилась наглухо, никаких вариантов ее поднять не было, причем и половина файлов даже пользовательских пропадало или переименовывалось, вообще файловая система была в шоке
    но проблема не в выключении питания оказывается, а в том, что при обращении к usb (serial), если вдруг отсоединить этот usb чтобы переписать скетч в arduino, то такого linux не любит, в отличии от windows, так что проблема решилась тем, что usb не отсоединяем пока не вырубим малину, уже две недели полет нормальный
     
  4. embed

    embed Нерд

    Наверное все-таки ошибки в вашей программе. У меня сейчас в работе проект, который использует одновременно 4 usb-serial. При отвале любого ни разу не было ни зависания малинки ни тем более краша файловой системы. Правда весь софт - на питоне, только пара мелких кусочков на C, но они не имеют отношения к usb-serial.
     
  5. ut2k5

    ut2k5 Нерд

    может и в программе проблема, собственно для этого и выложен ее код чуть Выше. питон не знаю, а вот С++ с университета использую, мне он и легче и удобнее, если можете показать как то же самое сделать на питоне, буду рад
     
  6. embed

    embed Нерд

    Код (Python):
    #!/usr/bin/python
    ###############################################################################

    import sys
    import serial
    import os

    link = serial.Serial(port="/dev/ttyUSB0", baudrate=9600)
    while True:
        s = link.readline()
        s1 = s.strip()
        if s1=="play_movie1":
            os.system(u"echo %s >> out.txt" % (s1))
            link.write(u"command %s accomplished\n" % (s1))
        elif s1=="play_movie2":
            os.system(u"echo %s >> out.txt" % (s1))
            link.write(u"command %s accomplished\n" % (s1))
        elif s1=="quit":
            break
        else:
            link.write(u"error command %s \n" % (s1))
            link.write(u"use commands: play_movie1|play_movie2|quit \n")
    link.close()
    Вместо строчек "echo %s >> out.txt" вставляете свои команды запуска плеера.
    по пустой команде выдает подсказку, по quit выходит.
    Код гарантировано рабочий, проверил.
     
  7. ut2k5

    ut2k5 Нерд

    спасибо, а программа будет работать в режиме консоли? мне так не подходит. нужно чтобы запускался отдельный процесс. данной решение используется в квест-руме, через 15 минут после запуска малины она уходит в спящий режим и экран черный, а запуск моей программы не выводит из спящего режима, таким образом не портит задачу, когда видео не воспроизводится - экран темный
     
  8. embed

    embed Нерд

    Спящий режим надо отключить конечно. Смотрите настройки Xserver.
    Моя программа будет работать как угодно, если ее правильно запустить. Чтобы работало в фоне надо чтобы программа ничего не выводила в консоль и запускалась в фоне. Это можно сделать строкой запуска типа
    python программа.py > /dev/null 2>&1 &
    > /dev/null - переназначение вывода в нулевое устройство
    2>&1 - переназначение ошибок туда же
    & - запуск в фоне