Парсинг данных

Тема в разделе "Arduino & Shields", создана пользователем sanik, 30 мар 2022.

  1. sanik

    sanik Гик

    Помогите пожалуйста разобраться! С софтого порта данные приходят целые ввожу например 1;455,413,290
    и так же вторые данные 0;12 из примера софт сериал приходят целыми
    Далее в моем коде данные в софт сериал приходят тоже целыми а в порт монитора после парсинга приходит не то что посылал протокол работает только числа не те что посылаю.
    Код (C++):

    #include <SoftwareSerial.h>
    SoftwareSerial softSerial(2, 3); // Создаём объект softSerial указывая выводы RX, TX

    #define LED_1 6
    #define LED_2 13

    int VelocityPars[] = {0, 0, 0};
    int SpeedPars[] = {0, 0, 0};
    int ledstate1;
    int ledstate2;
    byte Key;
    bool flagKey = 0;
    void setup() {
      softSerial.begin(38400);
      softSerial.setTimeout(10);
      Serial.begin(9600);
      pinMode(LED_1, OUTPUT);
      pinMode(LED_2, OUTPUT);
    }

    // протокол:
    // 0 - KEY (1-12)
    // 1 - SpeedPars (0..700,0..700,0..700)
    // 2 - VelocityPars (0..700,0..700,0..700)

    void loop() {
      if (softSerial.available()) {
        static byte prevAm = 0;
        static uint32_t tmr = 0;
        byte ad = softSerial.available();
        if (ad != prevAm) {
          prevAm = ad;
          tmr = millis();
        }
        if ((ad && millis() - tmr > 10) || ad > 60) {
          uint32_t us = micros();
          char str[30]; // указатели на строки
          int amount = softSerial.readBytesUntil(';', str, 30);
          str[amount] = NULL;
          softSerial.println(str);
          int ints[5];
          int count = 0;
          char* offset = str;
          while (true) {
            ints[count++] = atoi(offset);
            offset = strchr(offset, ',');
            if (offset) offset++;
            else break;
          }
          switch (ints[0]) {  // ключ
            case 0:
              Key = ints[1];
              flagKey = 1;
              Serial.println("KEY");
              Serial.println(Key);
              break;
            case 1:
              SpeedPars[0] = ints[1];
              SpeedPars[1] = ints[2];
              SpeedPars[2] = ints[3];
              Serial.println("Speed");
              for (int i = 0; i < 3; i++) {
                Serial.println(SpeedPars[i]);
              }
              break;
            case 2:
              VelocityPars[0] = ints[1];
              VelocityPars[1] = ints[2];
              VelocityPars[2] = ints[3];
              Serial.println("Velocity");
              for (int i = 0; i < 3; i++) {
                Serial.println(VelocityPars[i]);
              }
              break;
          }
        }

      }
      byte KEY = Key;
      if ((KEY == 6) && (flagKey == 1)) {
        flagKey = 0;
        ledstate2 = !ledstate2;
        KEY = 0;
        Serial.println(KEY);
      }
      if ((KEY == 5) && (flagKey == 1)) {
        flagKey = 0;
        ledstate1 = !ledstate1;
        KEY = 0;
        Serial.println(KEY);
      }
      digitalWrite(LED_2, ledstate2);
      digitalWrite(LED_1, ledstate1);
    }
    вот что наблюдаю в порте:
    upload_2022-3-30_23-4-7.png

    оно же:
    https://disk.yandex.ru/d/NU66mJ8APuWrTQ
     
    Последнее редактирование модератором: 31 мар 2022
  2. sanik

    sanik Гик

    Есть второй вариант работает аналогично
    Код (C++):
    #include "Parser.h"
    #include <SoftwareSerial.h>
    SoftwareSerial softSerial(2, 3); // Создаём объект softSerial указывая выводы RX, TX


    #define LED_1 6
    #define LED_2 13

    int VelocityPars[] = {0, 0, 0};
    int SpeedPars[] = {0, 0, 0};
    int ledstate1;
    int ledstate2;
    byte Key;
    bool flagKey = 0;
    void setup() {
      softSerial.begin(38400);
      softSerial.setTimeout(10);
      Serial.begin(9600);
      pinMode(LED_1, OUTPUT);
      pinMode(LED_2, OUTPUT);

    }

    // протокол:
    // 0 - KEY (1-12)
    // 1 - SpeedPars (0..7000,0..7000,0..7000)
    // 2 - VelocityPars (0..7000,0..7000,0..7000)

    void loop() {
      if (softSerial.available()) {
        static byte prevAm = 0;
        static uint32_t tmr = 0;
        byte ad = softSerial.available();
        if (ad != prevAm) {
          prevAm = ad;
          tmr = millis();
        }
        if ((ad && millis() - tmr > 10) || ad > 60) {
          uint32_t us = micros();
          void clear();
          char str[30]; // указатели на строки
          int amount = softSerial.readBytesUntil(';', str, 30);
          str[amount] = NULL;
          softSerial.println(str);

          Parser data(str, ',');
          int ints[5];
          int am = data.parseInts(ints);
          switch (ints[0]) {  // ключ
            case 0:
              Key = ints[1];
              flagKey = 1;
              Serial.println("KEY");
              Serial.println(Key);
              break;
            case 1:
              SpeedPars[0] = ints[1];
              SpeedPars[1] = ints[2];
              SpeedPars[2] = ints[3];
              Serial.println("Speed");
              for (int i = 0; i < 3; i++) {
                Serial.println(SpeedPars[i]);
              }
              break;
            case 2:
              VelocityPars[0] = ints[1];
              VelocityPars[1] = ints[2];
              VelocityPars[2] = ints[3];
              Serial.println("Velocity");
              for (int i = 0; i < 3; i++) {
                Serial.println(VelocityPars[i]);
              }
              break;
          }
        }

      }
      byte KEY = Key;
      if ((KEY == 6) && (flagKey == 1)) {
        flagKey = 0;
        ledstate2 = !ledstate2;
        KEY = 0;
        Serial.println(KEY);
      }
      if ((KEY == 5) && (flagKey == 1)) {
        flagKey = 0;
        ledstate1 = !ledstate1;
        KEY = 0;
        Serial.println(KEY);
      }
      digitalWrite(LED_2, ledstate2);
      digitalWrite(LED_1, ledstate1);
    }
    парсинг
    Код (C++):
    #ifndef Parser_h
    #define Parser_h
    // простой и быстрый парсер строк в отдельные строки и числа

    class Parser {
      public:
        Parser (char* data, char newDiv = ',') {
          buf = data;
          div = newDiv;
        }
        ~Parser() {
          clear();
        }
        void clear() {
          if (str) free(str);
        }
        int amount() {
          int i = 0, count = 0;
          while (buf[i++]) if (buf[i] == div) count++;  // подсчёт разделителей
          return ++count;
        }
        int split() {
          int am = amount();            // количество данных
          clear();                      // освобождаем буфер
          str = (char**)malloc(am * 2); // создаём буфер
          str[0] = buf;                 // строка 0
          int i = 0, j = 0;             // счётчики
          while (buf[i]) {              // пока не NULL
            if (buf[i] == div) {        // если разделитель
              buf[i] = NULL;            // меняем на NULL
              str[++j] = buf + i + 1;   // запоминаем начало строки
            }
            i++;
          }
          return am;
        }
        int16_t getInt(int num) {
          return atol(str[num]);
        }
        float getFloat(int num) {
          return atof(str[num]);
        }
        bool equals(int num, const char* comp) {
          return !strcmp(str[num], comp);
        }
        int parseInts(int* data) {
          int count = 0;
          char* offset = buf;
          while (true) {
            data[count++] = atoi(offset);
            offset = strchr(offset, div);
            if (offset) offset++;
            else break;
          }
          return count;
        }
        int parseBytes(byte* data) {
          int count = 0;
          char* offset = buf;
          while (true) {
            data[count++] = atoi(offset);
            offset = strchr(offset, div);
            if (offset) offset++;
            else break;
          }
          return count;
        }

        char* buf = NULL;
        char** str = NULL;

        char* operator [] (uint16_t idx) {
          return str[idx];
        }
        char div;
      private:
    };

    #endif
     
  3. SergeiL

    SergeiL Оракул Модератор

    Цифры в мониторе порта - это произвольные значения со стека (или ригистры), так как печатаются неинициализированные ячейки массива.
    Код:
    Код (C++):
          char str[30]; // указатели на строки
          int amount = softSerial.readBytesUntil(';', str, 30);
          str[amount] = NULL;
          softSerial.println(str);
     
    На входе данные: 1;455,413,290
    после выполнения этого кода в массиве будет:
    Код (C++):
    str[0]='1';
    str[1]=\0;
    Дальше:
    Код (C++):
         int ints[5];
          int count = 0;
          char* offset = str;
          while (true) {
            ints[count++] = atoi(offset);
            offset = strchr(offset, ',');
            if (offset) offset++;
            else break;
          }
     
    offset настраиваете на начало str[].
    в цикле в ints[0] записываете 1.
    Дальше в str[1] = \0

    Вызов:
    Код (C++):
    offset = strchr(offset, ',');
    ничего не найдет, так как в str[1] записан 0, а это признак конца строки.
    В offset будет записан \0
    Поэтому вы вывалитесь из цикла, и перейдете в switch()
    В нем вы ничего не проверяя печатаете:
    Код (C++):
              SpeedPars[0] = ints[1];
              SpeedPars[1] = ints[2];
              SpeedPars[2] = ints[3];
              Serial.println("Speed");
              for (int i = 0; i < 3; i++) {
                Serial.println(SpeedPars[i]);
              }
     
    ints[5] - массив локальный, он не инициализируется.
    В ints[1] - ints[3] вы ничего не записывали, поэтому печатается мусор со стека (или регистров)
     
    DetSimen, sanik и arkadyf нравится это.
  4. sanik

    sanik Гик

    Ничего не понял!
    Это что же получается я массив пытаюсь записать указатели?
    А как правильно записать массив ints[ ] ?
     
  5. SergeiL

    SergeiL Оракул Модератор

    Понятно..
    "Пальцем показать?" ©
     
  6. sanik

    sanik Гик

    Даже если носом тыкнуть не обижусь:) Пробовал перетащить в переменную Тоже и ничего не вышло Ну это и понятно
    Код (C++):
    for (int i = 0; i < count; i++) {
            ints[i] = str[i];
            Serial.println(data[i]);      
          }
    Не могу понять почему в str[0]='1'; Записывается как положено единица а в str[1] ничего?
    Буду очень признателен! четвертый день пытаюсь хоть что то получить.
     
  7. sanik

    sanik Гик

    Попытался записать data В порт стали приходить адекватные данные.
    Код (C++):

    while (true) {
            data[count++] = atoi(offset); // пишем число в буфер
            offset = strchr(offset, ','); // поиск следующей запятой
            if (offset) offset++;         // если это не NULL - продолжаем
            else break;                   // иначе покидаем цикл
          }
    for (int i = 0; i < count; i++) {
            ints[i] = data[i];
            Serial.println(ints[i]);
          }
    Ключ переключает switch Но все что прописано в нем не работает(
     
    Последнее редактирование: 1 апр 2022
  8. SergeiL

    SergeiL Оракул Модератор

    Что делает строка:
    Код (C++):
    int amount = softSerial.readBytesUntil(';', str, 30);
    То есть считываются данные до символа ';' и позже, в конце ставится \0. Все.
    Откуда взяться остальным данным до конца строки? (остальные три параметра).
     
  9. sanik

    sanik Гик

    Ага вот теперь боле менее стало понятно
    Я думал что она пропускает Символ ';',
    То есть мне нужно еще раз считать данные?
     
  10. sanik

    sanik Гик

    Похоже мне не дано разобраться Не подскажите как сделать?
     
  11. SergeiL

    SergeiL Оракул Модератор

    В вашу обработку millis(), я не вникал, там несколько запутано, править не стал. Значение micros() не используется, хотя получается.
    С "Key" - тоже не однозначно. Вы пишите, что Key 1-12:
    Код (C++):
    // протокол:
    // 0 - KEY (1-12)
    И в тоже время обрабатываете Key 0:
    Код (C++):
          switch (ints[0]) {  // ключ
            case 0:
              Key = ints[1];
              flagKey = 1;
              Serial.println("KEY");
              Serial.println(Key);
              break;
    Как я вижу очень хотите использовать:
    Код (C++):
    softSerial.readBytesUntil()
    Поэтому подправил только чтение параметров, остальное... все ваше:
    Код (C++):

    #include <SoftwareSerial.h>
    SoftwareSerial softSerial(2, 3); // Создаём объект softSerial указывая выводы RX, TX

    #define LED_1 6
    #define LED_2 13
    #define MAX_PARAMS 3

    int VelocityPars[MAX_PARAMS] = {0, 0, 0};
    int SpeedPars[MAX_PARAMS] = {0, 0, 0};
    int ledstate1;
    int ledstate2;
    byte Key;
    bool flagKey = 0;
    void setup()
    {
      softSerial.begin(38400);
      softSerial.setTimeout(10);
      Serial.begin(9600);
      pinMode(LED_1, OUTPUT);
      pinMode(LED_2, OUTPUT);
    }

    // протокол:
    // 0 - KEY (1-12)
    // 1 - SpeedPars (0..700,0..700,0..700)
    // 2 - VelocityPars (0..700,0..700,0..700)

    void loop() {
      if (softSerial.available()) {
        static byte prevAm = 0;
        static uint32_t tmr = 0;
        byte ad = softSerial.available();
     
        if (ad != prevAm) {
          prevAm = ad;
          tmr = millis();
        }
     
        if ((ad && millis() - tmr > 10) || ad > 60) {
          uint32_t us = micros();
          char str[30]; // массив для строки
          int ints[MAX_PARAMS];
          int count = 0;

          int amount = softSerial.readBytesUntil(';', str, 30);
          str[amount] = 0 ;
          softSerial.println(str);
          Key = atoi(str);

          if (amount && Key >= 0 && Key <= 12)
          {
            do
            {
              amount = softSerial.readBytesUntil(',', str, 30);
              str[amount] = 0;
              if (amount)
                ints[count] = atoi(str);
              else
                break;
            } while (++count < MAX_PARAMS);

            switch (Key) {  // ключ
              case 0:
                flagKey = 1;
                Serial.println("KEY");
                Serial.println(Key);
                break;
              case 1:
                Serial.println("Speed");
                for (int i = 0; i < count; i++) {
                  SpeedPars[i] = ints[i];
                  Serial.println(SpeedPars[i]);
                }
                break;
              case 2:
                Serial.println("Velocity");
                for (int i = 0; i < count; i++) {
                  VelocityPars[i] = ints[i];
                  Serial.println(VelocityPars[i]);
                }
                break;
            }
          }
          while (softSerial.available()) //скинем оставшиеся символы из буфера если key не входит в диапазон 0-12
            softSerial.read();
        }
      }

      byte KEY = Key;
      if ((KEY == 6) && (flagKey == 1)) {
        flagKey = 0;
        ledstate2 = !ledstate2;
        KEY = 0;
        Serial.println(KEY);
      }
      if ((KEY == 5) && (flagKey == 1)) {
        flagKey = 0;
        ledstate1 = !ledstate1;
        KEY = 0;
        Serial.println(KEY);
      }
      digitalWrite(LED_2, ledstate2);
      digitalWrite(LED_1, ledstate1);
    }
     
     
    sanik нравится это.
  12. sanik

    sanik Гик

    Спасибо вам огромное! Немного не то но теперь я уже разберусь что и как. Теперь мне понятно что делать! У меня было ints это ключ а Key это был байтовый параметр наподобие массива SpeedPars[] только приходило не три а один параметр.
     
  13. Igor68

    Igor68 Гуру

    Доброго времени суток! Простите, что выпимши пишу, но посему ВСЕ хотят символьный обмен?
    Мне по душе бинарный... я о том, что по аналогии не люблю Modbus ASCII и уважаю Modbus RTU. Текстовый то же хорошо, но для отладки. Ну вы сами представьте, на обеих сторонах надо вести преобразование текст<-->данные... если для ПК это "курить бамбук", то для МК это уже дополнительная работа!

    ЗЫ; простите, что встреваю, но 1Л кончился и я иду за вторым!:(