1. Форумный чат переехал в Telegram.
  2. В сообществе нашего форума Вконтакте создан раздел по продаже электронных компонентов.
    Каждый может продать в нем свои залежавшиеся детали. Подробности здесь.

Решено Прием данных по UART

Тема в разделе "Arduino", создана пользователем UL7AAjr, 20 ноя 2017.

  1. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.843
    Симпатии:
    311
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    В общем нужна помощь для создания скетча под Ардуину Pro Micro или Nano.

    У меня есть датчик передающий данные по UART в текстовом виде. Данные передаются периодически с интервалом примерно 2 сек. Каждая строка имеет определенный формат и выглядит например так:

    #_A:34_B:480_C:900_D:1200_T:245_@:F34D

    Нижнее подчеркивание обозначает символ табуляции 09, в конце строки стандартные 0A 0D
    цифры за буквами ABCD - значение канала, могут принимать значения от 0 до 2000. Т-температура в десятых долях градуса, @ - контрольная сумма в шестнадцатиричном представлении (тут это не нужно проверять, игнорируем).

    В общем требуется достаточно простая вещь:
    1. загрузить строку в буфер, проверить, что начинается с '#', иначе ждем другую строку (видимо начали ждать когда строка уже началась)
    2. разобрать строку и положить значения каналов в четыре переменных, температуру тоже (как есть). Все переменные целого типа.
    3. если значение, скажем канала "D" стало меньше заданного, например 500, то включить светодиод на 10 секунд, иначе перейти к пункту 1.
    4. перейти к пункту 1.

    Скетч нужен как пример кода работы с датчиком для Ардуино.

    Передающий UART настроен стандартно, 9600/N/8/1. Строка идет подряд, без пауз между символами.

    Ну вот примерно так. Помогите пожалуйста кто может и чем может:)
    Последнее редактирование: 21 ноя 2017
     
  2. radioengineer

    radioengineer Администратор Команда форума

    Сообщения:
    3.529
    Симпатии:
    345
    Адрес:
    Алматы
    Вот пока прием накидал, дальше нужно парсить и проверять на выполнение условия 3:
    PHP:
    #define MAX_BUFFER_SIZE        50                // Максимальное количество, принимаемых байт по UART.

    uint8_t  buf[MAX_BUFFER_SIZE];
    uint16_t byteCounter 0;
    bool     isRcvEnd    false;                // Флаг окончания приема строки по UART.

    void setup ()
    {
        
    Serial.begin(9600);
    }

    void loop ()
    {
        while (
    Serial.available() > 0) {
            if (
    Serial.read() == 0x23) {        // Здесь 0x23 символ начала пакета '#'.
                
    buf[byteCounter++] = 0x23;
            } else if (!
    byteCounter) {
                break;
            }

            
    buf[byteCounter] = Serial.read();

            if ((
    buf[byteCounter] == 0x0A) && (buf[byteCounter 1] == 0x0D)) {        // Признак конца пакета.
                
    byteCounter 0;
                
    isRcvEnd    true;
            } else {
                
    byteCounter++;
            }
        }

        if (
    isRcvEnd) {
            
    // Здесь парсим строку.
        
    }
    }
    --- Сообщения объединены, 21 ноя 2017 ---
    Также можно использовать функцию Serial.parseInt(), которая просто будет на лету парсить и возвращать целочисленное значение, но я с ней не работал, нюансов не знаю. В Arduino IDE есть пример, который тебе нужен, называется ReadASCIIString.
  3. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.843
    Симпатии:
    311
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Спасибо, с чтением строки более менее понятно. Парсинг строки уже от платформы не зависит, там понятно. Осталось разве что включить светодиод на 10 секунд и все. С этим тоже ясно. А на лету читать числа думаю не пойдет, вся строка в итоге нужна будет, контрольную сумму вдруг проверить.

    Вот только как-бы это на живом датчике испытать. Ардуину придется купить наверное:)
  4. radioengineer

    radioengineer Администратор Команда форума

    Сообщения:
    3.529
    Симпатии:
    345
    Адрес:
    Алматы
    Могу дать тебе Nano, если пойдет.
  5. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.843
    Симпатии:
    311
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Ну если никто готовый код не даст разве что. Так не хочется писать на С под Ардуино. Да и натупить по незнанию не хочется, все-же демка это.
  6. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.843
    Симпатии:
    311
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Вот реальный дамп данных
    Код:
    #    A:55    B:139    C:180    D:152    T:221    @:1DE1
    #    A:60    B:221    C:390    D:445    T:222    @:5E34
    #    A:51    B:199    C:429    D:655    T:222    @:41C4
    #    A:44    B:188    C:428    D:649    T:223    @:24AE
    #    A:40    B:175    C:424    D:641    T:223    @:9885
    #    A:41    B:173    C:430    D:655    T:225    @:5D79
    
    только вот табуляция на пробелы при вставке на форум поменялась. Приложил текстовый файл с табуляциями.

    Вложения:

    • 0tab.txt
      Размер файла:
      236 байт
      Просмотров:
      2
  7. radioengineer

    radioengineer Администратор Команда форума

    Сообщения:
    3.529
    Симпатии:
    345
    Адрес:
    Алматы
    Реализовал парсинг:
    PHP:
    #define MAX_BUFFER_SIZE        50              // Максимальное количество, принимаемых байт по UART.

    char     buf[MAX_BUFFER_SIZE];
    uint16_t byteCounter 0;
    bool     isRcvEnd    false;               // Флаг окончания приема строки по UART.

    uint16_t parseValue (char firstchar second)
    {
        
    String   inString    "";
        
    uint16_t value;
        
    uint16_t findFirst  findSymbol (first);
        
    uint16_t findSecond findSymbol (second);

        for (
    int i findFirst 2findSecond 1i++) {
            
    inString += buf[i];
        }

        
    value inString.toInt();

        return 
    value;
    }

    uint16_t findSymbol (char symbol)
    {
        
    uint16_t position 0;

        for (
    int position 0position byteCounterposition++) {
            if (
    buf[position] == symbol) {
                return 
    position;
            }
        }
    }

    void setup ()
    {
        
    Serial.begin(9600);
    }

    void loop ()
    {
        while (
    Serial.available() > 0) {
            
    buf[byteCounter] = Serial.read();

            if (
    buf[byteCounter] == 0x23) {     // Здесь 0x23 символ начала пакета '#'.
                
    byteCounter 0;
            }

            if ((
    buf[byteCounter] == 0x0A) && (buf[byteCounter 1] == 0x0D)) {     // Признак конца пакета.
                
    isRcvEnd true;
            } else {
                
    byteCounter++;
            }
        }

        if (
    isRcvEnd) {
            
    // Здесь парсим строку.
            
    int A parseValue ('A''B');
            
    int B parseValue ('B''C');
            
    int C parseValue ('C''D');
            
    int D parseValue ('D''T');
            
    int T parseValue ('T''@');
            
    Serial.print (A);
            
    Serial.print (" ");
            
    Serial.print (B);
            
    Serial.print (" ");
            
    Serial.print (C);
            
    Serial.print (" ");
            
    Serial.print (D);
            
    Serial.print (" ");
            
    Serial.print (T);
            
    Serial.println();
            
    isRcvEnd false;
        }
    }
    Подсовываю, твой файл и получаю ответ:
    DeepinScreenshot_выберите-область_20171121144234.png
  8. radioengineer

    radioengineer Администратор Команда форума

    Сообщения:
    3.529
    Симпатии:
    345
    Адрес:
    Алматы
    Финальная версия, используется, встроенный в плату светодиод.
    PHP:
    #define MAX_BUFFER_SIZE        50              // Максимальное количество, принимаемых байт по UART.

    char     buf[MAX_BUFFER_SIZE];
    uint16_t byteCounter 0;
    bool     isRcvEnd    false;               // Флаг окончания приема строки по UART.
    uint32_t startTime   0;

    uint16_t parseValue (char firstchar second)
    {
        
    String   inString    "";
        
    uint16_t value;
        
    uint16_t findFirst  findSymbol (first);
        
    uint16_t findSecond findSymbol (second);

        for (
    int i findFirst 2findSecond 1i++) {
            
    inString += buf[i];
        }

        
    value inString.toInt();

        return 
    value;
    }

    uint16_t findSymbol (char symbol)
    {
        
    uint16_t position 0;

        for (
    int position 0position byteCounterposition++) {
            if (
    buf[position] == symbol) {
                return 
    position;
            }
        }
    }

    void setup ()
    {
        
    Serial.begin (9600);
        
    pinMode (LED_BUILTINOUTPUT);
        
    digitalWrite (LED_BUILTINLOW);
    }

    void loop ()
    {
        while (
    Serial.available() > 0) {
            
    buf[byteCounter] = Serial.read();

            if (
    buf[byteCounter] == 0x23) {     // Здесь 0x23 символ начала пакета '#'.
                
    byteCounter 0;
            }

            if ((
    buf[byteCounter] == 0x0A) && (buf[byteCounter 1] == 0x0D)) {     // Признак конца пакета.
                
    isRcvEnd true;
            } else {
                
    byteCounter++;
            }
        }

        if (
    isRcvEnd) {
            
    // Здесь парсим строку.
            
    int A parseValue ('A''B');
            
    int B parseValue ('B''C');
            
    int C parseValue ('C''D');
            
    int D parseValue ('D''T');
            
    int T parseValue ('T''@');
            
    /*Serial.print (A);
            Serial.print (" ");
            Serial.print (B);
            Serial.print (" ");
            Serial.print (C);
            Serial.print (" ");
            Serial.print (D);
            Serial.print (" ");
            Serial.print (T);
            Serial.println();*/

            
    if (500) {
                
    digitalWrite (LED_BUILTINHIGH);
                
    startTime millis();
            }
           
            
    isRcvEnd false;
        }

        if (
    startTime) {
            if ((
    millis() - startTime) > 10000) {
                
    digitalWrite (LED_BUILTINLOW);
            }
        }
    }
  9. radioengineer

    radioengineer Администратор Команда форума

    Сообщения:
    3.529
    Симпатии:
    345
    Адрес:
    Алматы
    Наверное, по хорошему надо писать класс на c++ чтобы ардуинщики его подключали и работали с датчиком в более простой манере, к которой они привыкли.
  10. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.843
    Симпатии:
    311
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Да не, в качестве демки как раз то, что надо. Без всяких изысков и понятно. Спасибо большое!

    PS: Я бы конечно немного по другому парсинг строки сделал, чтобы не зависело от порядка и количества каналов, но это не критично.
    1Untitled-1.jpg
  11. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.843
    Симпатии:
    311
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Немного переделал код. Здесь положу, может кому для примера пригодится.


    Упростил разбор строки, добавил контроль размера буфера. Вынес переменные в глобальную секцию. Добавил настройки порогового значения, номера канал, время горения и время паузы. Настройки хранятся в EEPROM.

    PHP:
    #include <EEPROM.h>

    #define BUF_SIZE          80
    #define EEPROM_CHNL_NO    0
    #define EEPROM_CHNL_VALUE 1
    #define EEPROM_LITE_SEC   3
    #define EEPROM_PAUSE_SEC  4

    uint16_t  sensvalue[4];     // данные каналов влажности
    uint16_t  senstemper;       // температура
    uint8_t   buflen 0;       // кол-во загруженных байт в буфер
    char      buf[BUF_SIZE];    // буфер данных
    bool      bufready false// данные в буфере готовы
    char      rxin;             // принятый по RX байт 

    uint32_t  startTime 0;
    uint32_t  stopTime 0;
    uint32_t  pauseTime 0;
    uint32_t  liteTime 0;

    uint8_t   chnl_no;          // номер контрольного канала (0..3) 0 - верхний
    uint16_t  chnl_value;       // значение срабатывания
    uint8_t   lite_sec;         // время горения светодиода (сек)
    uint16_t  pause_sec;        // пауза перед повторным срабатыванием (сек)

    uint8_t   N;

    void ParseValue(uint16_t *value)
    {
      
    N++;
      if (
    buf[N] == ':') {
        
    N++;
        *
    value 0;
        while ((
    buflen) and (buf[N] >= '0') and (buf[N] <= '9')) {
          *
    value = *value 10 + (buf[N++] & 0x0F);
        }
      }
    }

    void setup()
    {
        
    Serial.begin (9600);
        
    pinMode (LED_BUILTINOUTPUT);
        
    digitalWrite (LED_BUILTINLOW);

        
    /*
        // пример записи настроек в EEPROM
        chnl_no = 2;
        chnl_value = 500;
        lite_sec = 10;
        pause_sec = 15;
        EEPROM.put(EEPROM_CHNL_NO, chnl_no);
        EEPROM.put(EEPROM_CHNL_VALUE, chnl_value);
        EEPROM.put(EEPROM_LITE_SEC, lite_sec);
        EEPROM.put(EEPROM_PAUSE_SEC, pause_sec);
        */

        // загрузка настроек из EEPROM
        
    EEPROM.get(EEPROM_CHNL_NOchnl_no);
        
    EEPROM.get(EEPROM_CHNL_VALUEchnl_value);
        
    EEPROM.get(EEPROM_LITE_SEClite_sec);
        
    EEPROM.get(EEPROM_PAUSE_SECpause_sec);

    }

    void loop() {

     
      while ((
    Serial.available() > 0)) {
        
    rxin Serial.read();
        if ((
    rxin == '#') or (buflen == BUF_SIZE)) {buflen 0;}
        if (
    rxin == 0x0A) {bufready true;}
        
    buf[buflen++] = rxin;
      }

      
    // если данные готовы
      
    if (bufready) {
        
    bufready false;

        
    // если строка получена и начинается с '#'
        
    if (buf[0] == '#') {
          
    // разбор поступивших данных
          
    0;
          while (
    buflen) {
            switch (
    buf[N]) {
              case 
    'A'ParseValue(&sensvalue[0]); break;
              case 
    'B'ParseValue(&sensvalue[1]); break;
              case 
    'C'ParseValue(&sensvalue[2]); break;
              case 
    'D'ParseValue(&sensvalue[3]); break;
              case 
    'T'ParseValue(&senstemper);  break;
            }
            
    N++;
          }

          
    // включение светодиода по заданному уровню канала
          
    if ((sensvalue[chnl_no] < chnl_value) and (startTime == 0) and (stopTime == 0)) {
            
    digitalWrite (LED_BUILTINHIGH);
            
    startTime millis();
            
    liteTime = (uint32_t)lite_sec 1000;
          }

          
    // отключение  светодиода через заданное время
          
    if (startTime) {
            if (
    millis() - startTime liteTime) {
                
    digitalWrite (LED_BUILTINLOW);
                
    startTime 0;
                
    // запустить паузу
                
    stopTime millis();
                
    pauseTime = (uint32_t)pause_sec 1000;
            }
          } 
           
          
    // окончание паузы
          
    if (stopTime) {
            if (
    millis() - stopTime pauseTime) {
                
    stopTime 0;
            }
          }

          
    // отладочный вывод данных
          
    Serial.print (sensvalue[0]);
          
    Serial.print (" ");
          
    Serial.print (sensvalue[1]);
          
    Serial.print (" ");
          
    Serial.print (sensvalue[2]);
          
    Serial.print (" ");
          
    Serial.print (sensvalue[3]);
          
    Serial.print (" ");
          
    Serial.print (senstemper);
          
    Serial.println();
        }
      }
    }
    PS: еще раз спасибо Антону за помощь:)

Поделиться этой страницей