Общение двух ардуино по Serial

prohor-nastya

✩✩✩✩✩✩✩
30 Ноя 2020
51
1
Здравствуйте!
С мастера на слейв отправляется сигнал вкл/выкл светодиод, в ответ (по плану) приходит : "Led is ON" or "Led is OFF"
но, при попытке связать две ардуино по softwareSerial ---> Serial получается такой результат в мониторе порта master:
Hello, friend?
Enter a code word ...
One
Access is allowed...
1ʑ⸮́ON

0ʑ⸮́off

1⸮⸮⸮́ON

0⸮⸮⸮́off

1⸮⸮⸮́ON

Почему-то часть символов приходит в виде значков.
Подскажите пожалуйста почему так получается...
Подключение как на картинке.
2023-02-07_18-32-20.jpg
Код:
ардуино1 "мастер"
master:
// C++ code
//
#include <SoftwareSerial.h>
String buffer = "";
String code = "";
boolean recievedFlag;

SoftwareSerial mySerial(2, 3); // RX, TX

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


  Serial.println("Hello, friend?");
  Serial.println("Enter a code word ...");
  do
  {
    if(code!= "")code = "";
     
    while (Serial.available()>0)
    {
      code += (char)Serial.read();
      recievedFlag = true;                   // поднять флаг что получили данные
      delay(10);                              // ЗАДЕРЖКА. Без неё работает некорректно!
    }
    if (recievedFlag)
    {                      // если данные получены
      Serial.println(code);               // вывести
      recievedFlag = false;                  // опустить флаг
     }
 
  }
  while(code != "One");
   Serial.println("Access is allowed...");
}

void loop()
{

  if (Serial.available()>0)
  {  
    char x = Serial.read();
     //mySerial.write(x);
     mySerial.println(x);
  }
   while (mySerial.available()>0)
    {
      buffer += (char)mySerial.read();
      recievedFlag = true;                   // поднять флаг что получили данные
      delay(10);                              // ЗАДЕРЖКА. Без неё работает некорректно!
    }
   if (recievedFlag)
    {                      // если данные получены
      Serial.println(buffer);               // вывести
      buffer = "";                          // очистить
      recievedFlag = false;                  // опустить флаг
     }
}
slave:
// C++ code
//
int x ;
bool flagSend = 1;
//String masON = "Led is ON";
//char masOFF[] = {'L','e','d',' ','i','s',' ','O','F','F'};
void setup()
{
  Serial.begin(9600);
  pinMode(13,OUTPUT);
}

void loop()
{

  if (Serial.available())
  {
     x =  Serial.read();
     flagSend = 0;
      //Serial.println(x);
   }

  if (x == '1' && flagSend == 0 )
  {
     digitalWrite(LED_BUILTIN, HIGH);
    Serial.write(x);
    Serial.println("Led is ON");
    flagSend = 1;
   }
  else if (x == '0' && flagSend == 0 )
   {
     digitalWrite(LED_BUILTIN, LOW);
     Serial.write(x);
     Serial.println("Led is off");
     flagSend = 1;
    }

}
Если закомментировать в слэйве строки 26 и 33 то первая буква приходит правильно, потом значки и и в конце On и off правильно...
 

prohor-nastya

✩✩✩✩✩✩✩
30 Ноя 2020
51
1
Вроде нет, классическая какая-то.
Сейчас уже не на работе...
Попробую обновить ide.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,267
1,303
Москва
Дело в том, что имено 2.+ у меня так выводило нормальные сообщения, а 1.8 выводило их нормально. С обновлением не уверен что выйдет.
 

prohor-nastya

✩✩✩✩✩✩✩
30 Ноя 2020
51
1
Arduino 1.8.19
Ну попытка не пытка, будем пробовать...
Я вот думаю, а не в softwareSerial дело?
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
@prohor-nastya,
Запустите на ПК терминал, который может выводить данные в HEX и посмотрите, какие байты реально приходят.
(терминал например Terminal v1.93b, есть и другие).
 
  • Лойс +1
Реакции: Старик Похабыч

Старик Похабыч

★★★★★★★
14 Авг 2019
4,267
1,303
Москва
Софтсериал на 9600 должен работать стабильно.
Попробуйте считывать не по байтам, а целиком строку https://www.arduino.cc/reference/en/language/functions/communication/serial/readstring/
И если отправляете данные через prinntln (именно с ln) есть serial.readstringuntil , там надо считывать строку до символа ln. Это сократит код.
 
  • Лойс +1
Реакции: prohor-nastya

prohor-nastya

✩✩✩✩✩✩✩
30 Ноя 2020
51
1
Да, завтра попробую ваши варианты. В IDE 2.03 еще хуже...
Попробовал передавать данные from Serial to Serial.
так вот при таком подключении код в мастере выполняется только до строк
Serial.println("Hello, friend?");
Serial.println("Enter a code word ...");
и все дальше не реагирует на ввод с монитора порта.
Если отключить на мастере RX то все начинает работать и даже светодиодом можно управлять.
Понятно, что то я не знаю про этот юарт, но не понятно, что...
Но ответ от слейва естественно не приходит.
в тинкеркаде и на физических платах все одинаково.
connection as in the picture, below:
2023-02-08_21-46-27.png
Code:
master:
// C++ code
// master

//#include <SoftwareSerial.h>
String buffer = "";
String code = "";
bool recievedFlag =0;

//SoftwareSerial mySerial(2, 3); // RX, TX

void setup()
{
  Serial.begin(9600);
 // mySerial.begin(9600);
 
 
  Serial.println("Hello, friend?");
  Serial.println("Enter a code word ...");
  do                                          // в этом разделе запрашиваем пароль
  {
    if(code!= "")code = "";                   // обнуляем строковую переменную
      
    while (Serial.available()>0)              // если есть данные на вход
    {
      code += (char)Serial.read();            // забиваем строковую переиенную символами
      recievedFlag = true;                    // поднять флаг что получили данные
      delay(10);                              // ЗАДЕРЖКА. Без неё работает некорректно!
    }
    if (recievedFlag)
    {                                          // если данные получены
      Serial.println(code);                    // вывести
      recievedFlag = false;                    // опустить флаг
     }
  
  }
   while(code != "One");                        // если в строку пришло слово One идем дальше
   Serial.println("Access is allowed...");
}

void loop()
{
 
 /* if (Serial.available()>0)
  {   
    char x = Serial.read();
     //mySerial.write(x);
     mySerial.println(x);
  }
  */
   while (Serial.available()>0)
    {
      buffer += (char)Serial.read();
      recievedFlag = true;                   // поднять флаг что получили данные
      delay(2);                              // ЗАДЕРЖКА. Без неё работает некорректно!
    }
   if (recievedFlag)                         // если данные получены
    {                             
      Serial.println(buffer);                // вывести
      buffer = "";                           // очистить
      recievedFlag = false;                  // опустить флаг
    }
}
slave:
// C++ code
//slave
int x = 0 ;
bool flagSend = 1;
//String masON = "Led is ON";
//char masOFF[] = {'L','e','d',' ','i','s',' ','O','F','F'};
void setup()
{
  Serial.begin(9600);
  pinMode(13,OUTPUT);
}

void loop()
{
 
   while (Serial.available()>0)
  {
     x =  Serial.parseInt();
     flagSend = 0;
     //Serial.println(x);
  
 
  if (x == 1 && flagSend == 0 )
  {
       digitalWrite(LED_BUILTIN, HIGH);
    //Serial.write(x);
    Serial.println("Led is ON");
    flagSend = 1;
   }
  else if (x == 2 && flagSend == 0 )
   {
       digitalWrite(LED_BUILTIN, LOW);
     //Serial.write(x);
     Serial.println("Led is off");
     flagSend = 1;
    }
  }
}
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
@prohor-nastya,
В данном случае логично, что не работает. Вы к одному порту мастера подключили два устройства ПК и Слейв. Мастер не может получить данные с ПК. В качестве эксперимента можно попробовать подключить TX Слейв - RX мастер через диод шотки катодом к слейву. Тогда может получится работа мастера на прием при ПООЧЕРЕДНОЙ отправке с ПК или Слейва.
 

prohor-nastya

✩✩✩✩✩✩✩
30 Ноя 2020
51
1
блин, так там кз получается... o_O
или я не правильно понял?

:LOL::LOL::LOL: в тинкеркаде с обычным диодом заработало. и сообщения приходят нормально...
завтра попробую на физических платах
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
@prohor-nastya,
Оптимально использовать маломощный Шотки (у меня работает), но может будет работать и с другими.
Учтите, что ПК и Слейв в таком случае должны отправлять данные на мастер только поочередно. А данные отправляемые мастером будут получать и ПК и Слейв.
 

prohor-nastya

✩✩✩✩✩✩✩
30 Ноя 2020
51
1
спасибо!физика процесса для меня остается загадкой, как теперь приходят с Слейва приходят сообщения если диод подключен к нему обратным оробразом. при подаче сигнала должна же зона запирания увеличиваться и гасить сигнал...:oops:
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
@prohor-nastya,
Откройте схему принципиальную UNO на CH340. Микросхема CH340 через резистор 1 КОм обеспечивает 5В на входе мастера, если на выходе слейва диод и установлена 1 (5В).
 

prohor-nastya

✩✩✩✩✩✩✩
30 Ноя 2020
51
1
@prohor-nastya,
Запустите на ПК терминал, который может выводить данные в HEX и посмотрите, какие байты реально приходят.
(терминал например Terminal v1.93b, есть и другие).
символьные строки по UART приходят без ошибок. но в использовании не очень удобно.
тоже самое программный UART отправляет с ошибкой.
Сегодня запустил терминальную программу Bray Terminal v1.9 и там действительно приходят второй и третий символ не понятные байты...
остальной текст правильный ... и два раза LF CR.
Еще не пробовал метод serial.readstringuntil , но по прогрммному UARTон наверное не подойдет, там же свои методы?
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
@prohor-nastya,
Посмотрел внимательнее ваше первое сообщение.
Действительно работать будет трудно предсказуемо. Т.к. Софт сериал не может одновременно отправлять и получать.
Вы с мастера по софт сериал отправляете как я понял пять байт на слейв. '0' -LF -CR - LF- CR
Слев как только получит 0 начинает отвечать мастеру в софт сериал мастера. Если мастер в это время сам отправляет байт из оставшихся LF -CR - LF- CR то данные будут портится.
Или отправляете с мастера на слейв только один символ без LF -CR. Если получили 0 или 1 то только их и отправляйте, остальное игнорируйте. Для одного байта так проще.
Если надо будет отправлять с мастера много байт, то надо на слейве ждать пока не придут все.
Повторюсь по софт сериал отправка и прем должны происходить в не пересекающиеся временные интервалы.
 

Kir356

✩✩✩✩✩✩✩
4 Апр 2023
1
0
Здравствуйте!
Тут проблема, на 2 физических платах Arduino UNO с подключенными к каждой по модулю SV610 передача и прием данных при запуске на обеих платах работает некорректно - данные с "мастера" отправляются по SV610 вроде правильно, но на "слейве" данные либо приходят но не следует ответа(при том светодиоды потухают), либо ответ со "слейва" идет, но "мастер" не получает данные, однако через какое-то время начинает работать корректно - начинают правильно и безперебойно отправлять данные и зажигать светодиоды по управляющим данным и в этот момент прибор может редко, однако опять ломаться и действовать по одному из описанных ранее сценариев.

Мне не ясно из-за чего происходит "прогрев" и почему прибор не работает корректно сразу, после запуска.


Code
Master:
#include <SoftwareSerial.h>


#define PIN_POT1 A0   // Пин 1 потанциометра
#define PIN_POT2 A1   // Пин 2 потанциометра
#define PIN_RX  7     // Пин RX для SoftwareSerial
#define PIN_TX  6     // Пин TX для SoftwareSerial
#define Set      4    // номер пина платы Ардуино к которому будет подключен разъем Set радиомодуля
#define Cs       5    // номер пина платы Ардуино к которому будет подключен разъем Cs радиомодуля


struct controlData     //Создаем структуру с типами переменных соответствующими формату передачи данных по радио
  {
   bool control_bit;   //бит определяет будет ответ от слейва или нет
   int data_pot1;      //Данные с 1 потанциометра
   int data_pot2;      //Данные со 2 потанциометра
   };

  struct telemetry
  {
  bool confirm_bit;    //бит для подтверждения приема и запрос отправки
  bool ledCondition;   //данные с кнопки "слейва"
  int pressure;        //Давление
  float temper;        //Температура
  };
 
SoftwareSerial MySerial(PIN_RX, PIN_TX);  //ставим пины 7(RX) и 6(TX) для SoftwareSerial
unsigned long time_dataRsv;               //время ожидания приема данных
bool timeOut=0;                           //превышен лимит времени ожидания приема данных 0 - нет, 1 - да

controlData myCntrlData;      //Объявляем управляющую структуру
telemetry myTelem;            //Объявляем структуру телеметрии


void setup()
{
  pinMode(PIN_POT1, INPUT);   //пин 1 потанциометра на ВХОД
  pinMode(PIN_POT2, INPUT);   //пин 2 потанциометра на ВХОД
 
  MySerial.begin(9600); 
  Serial.begin(9600);

  pinMode(Cs, OUTPUT);                   // Переводим вывод CS модуля в режим выход 
  pinMode(Set, OUTPUT);                  // Переводим вывод Set модуля в режим выход 
 
  digitalWrite(Cs, HIGH);                // Назначаем выводу уровень логической единицы - включаем модуль
  digitalWrite(Set, HIGH);               // Назначаем выводу уровень логическоЙ ЕДИНИЦЫ ДЛЯ ПЕРЕХОДА В РАБОЧИЙ РЕЖИМ
  delay(2000);

 myTelem.confirm_bit = 1;           //Отв. бит = 1
  myCntrlData.control_bit = 1;     //Упр. бит = 1
}



void loop()
{
  int pot1;
  int pot2;
 
  pot1 = analogRead(PIN_POT1);     //Читаем значение с 1 потанциометра
  pot2 = analogRead(PIN_POT2);     //Читаем значение со 2 потанциометра

  map(pot1, 0, 1023, 0, 255);      //Преобразовываем значения 1 потанциометра в рабочий диапозон
  map(pot2, 0, 1023, 0, 255);      //Преобразовываем значения 2 потанциометра в рабочий диапозон
 
  pot1 = constrain(pot1, 0, 255);  //дополнителяная проверка значения 1 потанциометра
  pot2 = constrain(pot2, 0, 255);  //дополнительная проверка значения 2 потанциометра

  myCntrlData.data_pot1 = pot1;    //записываем значения 1 потанциометра в управляющую структуру
  myCntrlData.data_pot2 = pot2;    //записываем значения 2 потанциометра в управляющую структуру
 
 if(myTelem.confirm_bit == 1)               
  {
    Serial.print("We are sending:");       
    Serial.print("\t\t");                    //Табуляция
    Serial.print(myCntrlData.data_pot1);                 //вывод на экран данных с 1 потанциометра
    Serial.print("\t\t");                    //Табуляция
    Serial.print(myCntrlData.data_pot2);                 //вывод на экран данных со 2 потанциометра
    Serial.print("\t\t");                    //Табуляция
    Serial.println(myCntrlData.control_bit);             //вывод на экран состояние упр. бита
    Serial.println(" ");
    
    MySerial.write((byte*)&myCntrlData, sizeof(myCntrlData));   //Отправляем управляющую структуру
    myTelem.confirm_bit = 0;                                    //Обнуляем отв. бит
  }
 
  if(myTelem.confirm_bit == 0)                          //если отв. бит равен 0
  {
   time_dataRsv = millis();                             //обновляем таймер
    while (1)                          //Бесконечный цикл
    {
      if((millis()-time_dataRsv) >= (unsigned long)100) //если прошло 100 мс
      {
       timeOut = 1;                                     //таймаут = 1
      }
      
      while(MySerial.available()>0)                          //пока ЧТО ТО ЕСТЬ НА ВХОД
      {
       MySerial.readBytes((byte*)&myTelem,sizeof(myTelem));  //читаем структуру телеметрии
      }
      
      if(myTelem.confirm_bit ==1)      break;                //если отв. бит равен 1, выйти из цикла
      if(timeOut == 1)            break;                     //если таймаут равен 1, выйти из цикла
     }
      Serial.print("We are resived:");
      Serial.print("\t\t");            //Табуляция
      Serial.print(myTelem.pressure);               //вывести на экран давление
      Serial.print("\t\t");            //Табуляция
      Serial.print(myTelem.temper);                 //вывести на экран температуру
      Serial.print("\t\t");            //Табуляция
      Serial.println(myTelem.ledCondition);         //вывести на экран данные от кнопки слейва
      Serial.println(" ");
   }
 if (timeOut == 1)                    //если таймаут равен 1
  {
      Serial.println("not Response"); //Нет ответа
      myTelem.confirm_bit = 1;        //отв. бит = 1
      timeOut = 0;                    //обнуляем таймаут
  }
  
}
Slave:
#include <SoftwareSerial.h>

#define PIN_LED_1 9   // Светодиод реагирующий на пришедшие данные
#define PIN_LED_2 10   // Светодиод реагирующий на пришедшие данные
#define PIN_BTN 8   // Кнопка
#define PIN_RX  7   // Пин, используемый как RX для SoftwareSerial
#define PIN_TX  6   // Пин, используемый как TX для SoftwareSerial
#define Set      4  // номер пина платы Ардуино к которому будет подключен разъем Set радиомодуля
#define Cs       5  // номер пина платы Ардуино к которому будет подключен разъем Cs радиомодуля

  struct controlData   //Создаем структуру с типами переменных соответствующими формату передачи данных по радио
  {
    bool control_bit;      //управляющий бит 1 - выполняем принятые команды и отправляем телеметрию, если 0 - стоим ждем
    int cond_Led1;         //управляющий ШИМ для 1го светодиода
    int cond_Led2;         //управляющий шим для 2го светодиода
  };

  struct telemetry
  {
    bool confirm_bit;      //бит подтверждения приема - слейв принял и выполнил
    bool ledCondition;     
    int pressure;
    float temper;
  };
 
   SoftwareSerial MySerial(PIN_RX, PIN_TX);
  
   controlData myContrlData;  //Объявляем управляющую структуру
   telemetry myTelem;         //Объявляем структуру телеметрии
   bool flagBut = false;         //флаг кнопки
   unsigned long timerMotor;      //таймаут работы моторов - если время ожидания управляющего сигнала превысило 100мс то стоп
      
void setup()
{
  MySerial.begin(9600);
  Serial.begin(9600);
 
  pinMode(PIN_BTN, INPUT_PULLUP);
  pinMode(Cs, OUTPUT);                  // Переводим вывод CS модуля в режим выход 
  pinMode(Set, OUTPUT);                  // Переводим вывод Set модуля в режим выход 
 
  digitalWrite(Cs, HIGH);                // Назначаем выводу уровень логической единицы - включаем модуль
  digitalWrite(Set, HIGH);                // Назначаем выводу уровень логическоЙ ЕДИНИЦЫ ДЛЯ ПЕРЕХОДА В РАБОЧИЙ РЕЖИМ
 
  delay(2000);
  myTelem.confirm_bit = 1; //заполняем структуру данных для отправки
  myTelem.ledCondition = 0;
  myTelem.pressure = 1250; 
  myTelem.temper = 25.95;

 
}



void loop()
{
  bool btnState = !digitalRead(PIN_BTN); //читаем состояние кнопки

   if (btnState && !flagBut)
  {  // обработчик нажатия
    flagBut = true;
    myTelem.ledCondition = !myTelem.ledCondition; //меняем значение переменной в структуре по нажатию
  }
 
  if (!btnState && flagBut)
  {  // обработчик отпускания
    flagBut = false; 
  }
 
  myTelem.pressure++; //обновляем данные для телеметрии
  myTelem.temper++;
  if(myTelem.pressure == 25000) //если переменная структуры "давление" переполнена
  {
    myTelem.pressure=0; //обновляем данные для телеметрии
    myTelem.temper=0;   
  }
 
    while(MySerial.available()>0) //пока ЕСТЬ ЧТО ТО НА ВХОД
    {
      MySerial.readBytes((byte*)&myContrlData,sizeof(myContrlData)); //читаем управляющую структуру
    }
    
  if(myContrlData.control_bit == 1) //если упр. бит равен 1
    {
      /*
       *Serial.print(myContrlData.cond_Led1);   //выводим данные с первого потанциометра
       *Serial.print(" ");                      //разделяем пробелом
       *Serial.println(myContrlData.cond_Led2); //выводим данные со второго потанциометра и переходим на след. строку
     */
      timerMotor = millis();                            //Обновляем таймер
      MySerial.write((byte*)&myTelem, sizeof(myTelem)); //Отправляем структуру телеметрии
      myContrlData.control_bit = 0;                     //Обнуляем упр. бит
    }
  if(millis() - timerMotor > (unsigned long)100)        //Если прошло 100 мс
  {
    Serial.println("No command data");
    analogWrite(PIN_LED_1, 0);                 //Выключаем светодиоды
    analogWrite(PIN_LED_2, 0);                 
  }
 else
  {
   Serial.println("Got command data");
   analogWrite(PIN_LED_1, myContrlData.cond_Led1);   //включаем 1 светодиод с силой, назначенной 1 потанциометром
   analogWrite(PIN_LED_2, myContrlData.cond_Led2);   //включаем 2 светодиод с силой, назначенной 2 потанциометром
  }
}
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
но на "слейве" данные либо приходят но ...
Посмотрите (выводом в serial), что приходит на слейв, возможно увидите проблему.

Я не разбираюсь в особенностях работы SV610 (а они наверняка есть), поэтому замечания просто по Serial
Т.к. пока не известно, что приходит на слейв при "не работе", то дальше просто предположения, которые может и не влияют на ошибку.
У вас нет очистки буфера приема в случае сбоя. Т.е. если по какой то причине передастся например на 1 байт больше, чем ожидается, то 1 байт может остаться не прочитанным и дальнейшее чтение буде продолжаться со сдвигом на 1 байт.
Для очистки буфера, как вариант можно добавить dealy(20); Для проводного Serial было бы достаточно dealy(2); Но я не понял какие задержки будут у RF, поэтому 20 взято, чтобы наверняка.
while(MySerial.available()>0)
{
MySerial.readBytes((byte*)&myContrlData,sizeof(myContrlData));
dealy(20);
}
 

prohor-nastya

✩✩✩✩✩✩✩
30 Ноя 2020
51
1
Здравствуйте!
SV610, как я понял, при приеме данных по воздуху, автоматом отправляет эти данные в буфер Serial.
простите, а какая связь между delay и очисткой буфера?
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
какая связь между delay и очисткой буфера?
Например вы на слейве ждете 10 байт, мастер отправил 11. Предположим вы зашли в while когда уже available 5 байт. По readBytes считывается с ожиданием 10 байт, и выполняется очередная проверка while available()>0 Но очередной 11 байт еще только принимается, поэтому available()==0 и цикл while завершается, а принятый через некоторое время байт останется в буфере.
С конкретном случае, когда оправка пакетов происходит примерно раз в 0.1 сек, а ожидание readBytes длится 1 сек, вероятность сбоя повышается. Предположим слейвом был принят сбойный огрызок пакета в 3 байта. Тогда слейв 1 секунду ждет в readBytes прихода еще 7 байт. Мастер, не получив ответ от слейва посылает очередной корректный пакет в 10 байт, из которого при приеме на слейве, первые 7 байт дополняют ранее принятые 3, и пакет становится ошибочным. Последние 3 байта из исходно корректного пакета потом примутся и остаются в буфере и так далее.
 
  • Лойс +1
Реакции: prohor-nastya