Проблемы с Serial и другие приключения

Тема в разделе "Arduino & Shields", создана пользователем Maximus, 28 окт 2017.

Метки:
  1. Maximus

    Maximus Нуб

    Добрый день!
    Суть такая, у меня есть одна мега и нано. В будущем будет температурный контроллер китайского производства. Хочу мегу подрубить к нане через RS485, а на нано повесить некоторое оборудование. Так же, уже написал часть программы на Qt , Мегу хочу держать подключенной к USB и общаться. Например с программы на PC выставили значения, они уходят в большем пакете в Мегу, В меге этот пакет обрабатывается и отсылается на китайский контроллер или на ардуино нано. Начал пока с общение мега-нано. Но проблема такая, я с меги отсылаю пакет, на нано он читается хорошо. Нано как прочитала пакет, должна скопировать значения, заполнить свой пакет и отправить на мегу. Но мега вообще не читает ничего, как будто ничего не приходит!! ПРошу помочь!
    Так же с лампочкой которой управляет нано по команде меги, есть проблемы, не очень корректно работет.
    ПРошу помочь, посмотреть мой код и навести на мысль
    Ардуино Мега:
    Код (C++):

    #include <SoftwareSerial.h>

    #define DIR 8 // переключатель прием\передача
    #define BUT 49


    struct packet_big //пакет PC
    {
      byte start_byte_one;//в длинном пакете равен 254
      byte start_byte_two;//в длинном пакете равен 232
      byte temp_pomp;//температура охлаждения
      byte on_of_pomp;//on-off насоса (1 или 0)
      byte ex_temp_reactor;//текущая температура в реакторе
      byte current_temp_reactor;//выставленная температура в реакторе
      byte timer_ex;//таймер
      byte tmp;//пока не трогаем
      byte crv;//пока не трогаем
    };

    struct packet_arduino_pomp //пакет Arduino nano
    {

      byte start_byte_one;//в коротком пакете равен 212
      byte start_byte_two;//в коротком пакете равен 211
      byte temp_pomp;//температура охлаждения
      byte on_of_pomp;//on-off насоса (1 или 0)
      byte tmp;//пока не трогаем
      byte CRC8;//пока не трогаем
      /*упаковать*/
      void pask()
      {
        CRC8 = (start_byte_one + start_byte_two + temp_pomp + on_of_pomp + tmp) / 10;
      }
      /*проверка упаковки*/
      bool test_pask()
      {
        if (CRC8 == (start_byte_one + start_byte_two + temp_pomp + on_of_pomp + tmp) / 10) return 0; //ОК
        return -1; // ошибка
      }
    };

    packet_big one = {254, 232, 0, 0, 0, 0, 0, 0, 0};
    packet_arduino_pomp one_pomp = {212, 211, 0, 0, 0, 0};


    byte buttonState = 0;

    void setup()
    {
      Serial.begin(9600);//на PC
      Serial2.begin(9600); // SoftwareSerial на Arduino nano
      pinMode(BUT, INPUT);
      pinMode(DIR, OUTPUT);
    }

    void loop()
    {
      digitalWrite(DIR, HIGH); // включаем передачу
      buttonState = digitalRead(BUT); //кнопка
      byte* ptr28 = (byte* )&one;
      byte *ptr_pomp = (byte*)&one_pomp;

      if (buttonState == HIGH) //вкл/выкл от кнопки
      {
        if (one.on_of_pomp == 0) //если выключен , то перезаписываем в пакете (включаем)
          one.on_of_pomp = 1;
        else if (one.on_of_pomp == 1) //если включен, то перезаписываем в пакете (выключаем)
          one.on_of_pomp = 0;
      }

      one_pomp.on_of_pomp = one.on_of_pomp;

      // Serial.write(ptr28,sizeof(packet_big)); //передаем на PC
      one_pomp.pask();
      Serial2.write(ptr_pomp, sizeof(packet_arduino_pomp)); //передаем на Arduino nano

      delay(200);
      digitalWrite(DIR, LOW); // включаем прием
      //прием с PC данных всех команд, включая информацию о температуре с реактора
      if (Serial.available() > sizeof(packet_big)) //возможно установить колличество байт
      {
        byte buf_big = Serial.read();
        if (buf_big  == 254)
        {
          buf_big = Serial.read();
          if (buf_big  == 232)
          {
            ptr28[0] = 254;
            ptr28[1] = buf_big;
            for (size_t i = 2; i < sizeof(packet_big); i++)
            {
              buf_big = Serial.read();
              ptr28[i] = buf_big ;
            }
          }
        }
      }
      //возможно нужно повторно записать стартовый бит.
      //прием с ардуино nano данных о вкл/ вкл насоса и температуры охлаждения

      if (Serial2.available() >= sizeof(packet_arduino_pomp))
      {

        byte buf_pump = 0;
        for (size_t i = 0; i < sizeof(packet_arduino_pomp); i++)
        {
          Serial.println("1");
          if (i == 0 && (buf_pump = Serial2.read()) != 212)
          {
            Serial.println("2");
            i = 0;
            continue;
          }
          if (i == 1 && (buf_pump = Serial2.read()) != 211)
          {
            Serial.println("3");
            i = 0;
            continue;
          }
          if (i >= 2)
          {
            Serial.println("4");
            buf_pump = Serial2.read();
            ptr_pomp[i] = buf_pump;
            if (i == sizeof(packet_arduino_pomp) - 1)
            {
              Serial.println("5");
              if (one_pomp.test_pask()) //проверяем контрольную сумму
              {
                Serial.println("6");
                i = 0;
                continue;
              }
            }

          }
        }
        Serial.println("ON");
        for (size_t i = 0; i < sizeof(packet_arduino_pomp); i++)
        {
          Serial.println( ptr_pomp[i]);
        }
        Serial.println("END");

      }

      // запись данных из пакета от PC в пакет от ардуино и наобарот.
      // one.temp_pomp = one_pomp.temp_pomp;


    }
     
    Ардуино нано:
    Код (C++):
    #include <SoftwareSerial.h>

    #define DIR 8 // переключатель прием\передача
    #define ON 1 // вкл. клапан
    #define OFF 0 // вкл. клапан

    struct packet_arduino_pomp //пакет Arduino nano
    {

        byte start_byte_one;//в коротком пакете равен 212
        byte start_byte_two;//в коротком пакете равен 211
        byte temp_pomp;//температура охлаждения
        byte on_of_pomp;//on-off насоса (1 или 0)
        byte tmp;//пока не трогаем
        byte CRC8;//пока не трогаем
        /*упаковать*/
        void pask()
        {
            CRC8 = (start_byte_one + start_byte_two + temp_pomp+on_of_pomp+tmp)/10;
        }
        /*проверка упаковки*/
        bool test_pask()
        {
            if (CRC8 == (start_byte_one + start_byte_two + temp_pomp+on_of_pomp+tmp)/10) return 0; //ОК
            return -1; // ошибка
        }
    };


    static packet_arduino_pomp one = {212,211,0,0,0,0};//пакет с данными
    byte temp_pomp = 0;//переменная для хранения температуры

    SoftwareSerial RS485 (2, 6); // RX, TX

    void setup()
    {
        Serial.begin(9600);
        RS485.begin(9600); // SoftwareSerial

        pinMode(DIR, OUTPUT);
        pinMode(3, OUTPUT);
    }

    void loop()
    {

        byte* ptr28 = (byte* )&one;
        digitalWrite(DIR, LOW); // включаем прием
        delay(200);
        if (RS485.available()>= sizeof(packet_arduino_pomp))
        {


            byte buf_pump=0;
            for (size_t i = 0; i < sizeof(packet_arduino_pomp); i++)
            {
              Serial.println("1");
                if(i==0&&(buf_pump = RS485.read())!=212)
                {
                  Serial.println("2");
                    i=0;
                    continue;
                }
                if(i==1&&(buf_pump = RS485.read())!=211)
                {
                  Serial.println("3");
                    i=0;
                    continue;
                }
                if(i>=2)
                {
                  Serial.println("4");
                    buf_pump = RS485.read();
                    ptr28[i] = buf_pump;
                    if(i==sizeof(packet_arduino_pomp)-1)
                    {
                      Serial.println("5");
                        if(one.test_pask())//проверяем контрольную сумму
                        {
                          Serial.println("6");
                            i=0;
                            continue;
                        }
                    }

                }
            }
            Serial.println("ON");
            for(size_t i = 0; i < sizeof(packet_arduino_pomp); i++)
            {
                Serial.println(ptr28[i]);
            }
            Serial.println("END");
            if(one.on_of_pomp==ON) // включим клапан
            {
                digitalWrite(3, HIGH); //  включим клапан
            }
            else if (one.on_of_pomp==OFF)
                digitalWrite(3, LOW);
        }
        one.temp_pomp = 55;//записываем значение температуры охлаждения в пакет на отправку
        one.pask();//формируем контрольную сумму
        delay(50);
        digitalWrite(DIR, HIGH); // включаем передачу
    RS485.write(ptr28,sizeof(packet_arduino_pomp));
    delay(50);

    }
     
     
  2. b707

    b707 Гуру

    не вполне понятно, почему вы называете обычный сериал именем Rs485. Это совершенно разные протоколы.
    При беглом проглядывании кода ошибок не видно ( если считать, что там нет rs485, а есть обычный сериал). Думаю, ошибки проще всего искать вам самому - напихайте в код побольше отладочного вывода и смотрите.
     
  3. Maximus

    Maximus Нуб

    Убераю все delay, все работает, но не долго, быстро уходит в штопор и начинает зацикливатся тчо на меге, что на нано в условии чтения :
    Код (C++):
    if (i == 1 && (buf_pump = Serial2.read()) != 211)
          {
            Serial.println("3");
            i = 0;
            continue;
          }
    Что это могла быть?
     
  4. b707

    b707 Гуру

    Ну так и должно быть...
    В этом куске кода может и даже ДОЛЖНО зацикливаться. Условием выхода из цикла у вас является последовательное появление байт 211 и 212, верно? - Но вы же не проверяете, есть ли еще символы в Сериале... Когда буфер сериала исчерпан, функция read() начинает возвращать -1, а программа бесконечно читает пустой сериал и ищет значение 211
     
    arkadyf и Tomasina нравится это.
  5. DIYMan

    DIYMan Guest

    Обычная практика при работе с RS-485, помимо правильного переключения приём/передача - это ОБЯЗАТЕЛЬНО дожидаться завершения передачи по UART, например, так:
    Код (C++):
    // ждём завершения передачи по UART Serial3
    while(!(UCSR3A & _BV(TXC3) ));
     
     
  6. Tomasina

    Tomasina Сушитель лампочек Модератор

    а что является признаком завершения передачи?
     
  7. brokly

    brokly Гик

    Дык DIYman вроде напсал, флажек TXC3 в регистре UCSR3A, для третьего сериала.... Для других заменить цифирьку 3 на соответствующий номер 0 или 1 или 2.

    Можно использовать и такую гадость как:
    Код (C++):
    // переключиться на передачу
    Serial.print("Blablablabla");
    Serial.flush();
    delay(1); // задержка должна быть равна времени передачи одного байта на текущей скорости
    // тут переключиться на прием
    Еще вариант:
    Код (C++):
    uint16_t temp=Serial.availableForWrite();
    // переключиться на передачу
    Serial.print("Blablablabla");
    while (Serial.availableForWrite()>=temp);
    delay(1); // задержка должна быть равна времени передачи одного байта на текущей скорости
    // тут переключиться на прием
     
    Последнее редактирование: 30 окт 2017
    DIYMan и Tomasina нравится это.
  8. DIYMan

    DIYMan Guest

    Совсем не гадость, я просто забыл упомянуть - помимо флага очистки буфера в нужном UART - я ишшо жду таймаута при чтении, равного времени передачи нескольких байт (зависит от специфики). Т.е. по завершению передачи сразу переключаюсь на приём - ну а там в цикле - таймаут ловлю уже.

    Ту же фишку можно проделать и при помощи delay, как вы и описали - просто она не обеспечивает всей гибкости - мало ли что там надо ещё сделать, ожидаючи - ответил ли клиент. И хотя я и активно юзаю yield для критичных ко времени задач - всё равно находятся дела, которые можно поделать в цикле, ждучи, пока либо ведомый просрётся пакетом в шину, либо - наступит звездец в виде таймаута.