Последовательная связь строк

Я использовал этот простой код, чтобы понять основы последовательной связи. Это обеспечивает простую связь между компьютером и arduino:

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

void loop() {
  if(Serial.available()>0)  //проверка наличия каких-либо полученных данных
  {   
      receive_buffer = Serial.readString();  //read received data      
      Serial.print("received data is: ");
      Serial.println(receive_buffer);        //отображение полученных данных
  }

}

Это работает. Теперь моей следующей идеей было попытаться связаться между двумя ардуино. Я использовал этот код для приемника arduino.

Отправитель arduino содержит двигатель, оснащенный энкодером, и, используя значение ppr, я вычисляю обороты двигателя. Код для rpm работает отлично. Теперь я хочу передать эти данные получателю.

Проводка: (RX,TX) отправителя, подключенного к (TX,RX) приемника, и GND становится общим.

А теперь вперед arduino.cc, он говорит, что Serial.write() может иметь строку в качестве входа. Итак, я попробовал ввести значение rpm (которое было int) в строку, а затем отправить эту строку получателю через Serial.write().

float ppr=512.0;
int encpin=3;
volatile long pulsecount=0;
float revs;
float rps;
int rpm;


extern volatile unsigned long timer0_millis;


void setup()
{
  pinMode(encpin,INPUT_PULLUP);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(encpin),function,RISING);
                                        //при обнаружении импульса
                                           //срабатывает прерывание,
//вызывается функция  
}
//приращение импульса на одну
void function(){
  pulsecount+=1;
}


void loop()
{
 revs= pulsecount/ppr ;
 if(millis()>=1000)
  {
    rps= revs;
    rpm=rps*60;
    pulsecount=0; //сбрасывает pulsecount
    
    String strrpm= (String)rpm;
   
   
    //Serial.println(rpm);
    Serial.write(strrpm);
   
  
   // Serial.println("string rpm");
    //Serial.println(strrpm);
   
    noInterrupts ();  //resests millis() to 0
    timer0_millis = 0;
    interrupts ();
  }
}

Однако я получаю ошибку:

43:24: error: no matching function for call to 'HardwareSerial::write(String&)'

Проблема не в типизации. Я попытался просто распечатать strrpm, и это сработало отлично. При попытке Serial.write() с другой и простой строкой, т. е. Serial.write("abcdef"); , он все еще давал ту же ошибку.

Как тогда я должен отправить свое значение strrpm (string rpm) в приемник?

, 👍0


1 ответ


Лучший ответ:

1

Serial.write() предназначен для отправки двоичных данных. Поэтому, если вы хотите отправить строку с ним, вам нужно предоставить c-строку, а не реализацию Arduino класса String. Это легко сделать с помощью функции-члена этого класса:

Serial.write(strrpm.c_str());

Это успешно компилируется для меня.

Хотя так делать не стоит. Класс String обычно плохо работает на небольших микроконтроллерах, особенно на микроконтроллерах AVR с малой памятью (например, Uno, Nano, Mega,...), потому что он вызывает фрагментацию памяти (см. Majenkos "The Evils of Arduino Strings").

Вместо этого следует просто использовать соответствующие функции print() класса Serial. Они сделаны для отправки читаемых человеком строк. Нет необходимости сначала создавать строковый объект. Просто сделайте

Serial.println(rpm);

И он будет выполнять само преобразование строк без использования класса String.


Кроме того, у меня есть несколько заметок о вашем коде:

  • То, как вы используете функцию millis (), немного странно. Нет необходимости на самом деле сбрасывать само значение millis (). Обычно вы держите метку времени в переменной, а затем вычисляете только разницу между millis() и меткой времени. Это также безопасно для переполнения. Вот так

      unsigned long timestamp = 0;
    
      ...
      if(millis()-timestamp >= 1000){
          ...
          timestamp+=1000;
      }
    

    Это не использует переменную extern, это не сброс счетчика millis () (который также может быть связан с другими вещами, которые полагаются на него) и является несколько стандартным способом сделать это.

  • Вы используете атомарный раздел для сброса счетчика millis (), но не для доступа к pulsecount. Это на самом деле то место, где вам это абсолютно необходимо. pulsecount имеет тип long, поэтому ему потребуется много инструкций для расчета оборотов. Процедура обслуживания ваших прерываний может легко испортить данные в этот момент, что приведет к искажению данных. Переместите вычисление в оператор if и используйте атомарный раздел, как со счетчиком millis (), чтобы прочитать его в локальную переменную, тогда вы можете выполнить вычисления. Это сохранит время, когда прерывания выключены очень коротко. Вы также можете - вместо того, чтобы выключать все прерывания, - просто отсоединить прерывание pin на это время, чтобы другие вещи не были затронуты. Вот такой код:

      void loop(){
          if(millis()-timestamp > 1000){
               // deactivate our pin interrupt, so it cannot mess with the data
               detachInterrupt(digitalPinToInterrupt(encpin));
               // скопируйте pulsecount в локальную переменную для дальнейшего расчета
               long local_pulsecount = pulsecount;
               // повторная активация нашего PIN-прерывания
               attachInterrupt(digitalPinToInterrupt(encpin),function,RISING);
               // Сделать расчет и распечатать значение
               ...
               timestamp += 1000;
           }
       }
    
,

"Вместо этого вы должны просто использовать соответствующие функции print() класса Serial. Они созданы для отправки удобочитаемых строк. Нет необходимости сначала создавать строковый объект. Просто сделайте Serial.println(об / мин)" Но я не понимаю, как это заставляет ардуино общаться?, @satan 29

Кроме того, ваше первоначальное предложение: код успешно компилируется и выводит значения на последовательный монитор отправителя: но последовательный монитор получателя пуст, @satan 29

'Serial.println() уже отправит данные в удобочитаемой для человека форме через serial. Таким образом, это уже общение. Пожалуйста, попробуйте использовать " Serial.readStringUntil('\n') вместо " Serial.ReadString ()". У последнего есть тайм-аут 1 сек. Поскольку вы отправляете каждую секунду, между каждой передачей менее 1 секунды. Это может привести к тому, что эта функция никогда не выйдет. Предыдущий вызов функции будет считываться только до символа новой строки, который автоматически добавляется функцией `println (), @chrisl