Как читать и записывать на устройство rs232 с/на Arduino

Я хотел бы считывать данные с устройства rs232 (измеритель уровня звука SL-5868P).
протокол и многое другое : https://sigrok.org/wiki/Colead_SL-5868P.

У меня уже есть скрипт Python для чтения данных с помощью преобразователя TTL в USB (без Arduino). Теперь я также хочу иметь возможность считывать показания счетчика с помощью Arduino.

Я купил преобразователь RS232 в TTL (MAX3232). Я получаю либо очень странные символы (скорость передачи данных установлена правильно), либо не логические числа. С чипом MAX я вообще не получаю никаких данных.

  1. Чтобы узнать, готовы ли данные, счетчик должен отправить 0x10.
  2. Чтобы получить данные, отправьте 0x20.

Не согласится ли кто - нибудь мне немного помочь? Это было бы очень мило. Может быть, есть кто-то, кто имеет опыт чтения (и записи) RS232. Единственное, что имеет значение, - это как я могу получить эти данные. Остальное я знаю, что нужно делать с программным обеспечением (конвертация и т.д.). Я не очень хорошо знаком с Arduino .

Приведенный ниже код является примером из SoftwareSerial (немного измененным). Я использую Arduino Uno.

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup() {
  // Откройте последовательную связь и дождитесь открытия порта:
  Serial.begin(2400);
  while (!Serial) {
    ; // дождитесь подключения последовательного порта. Требуется только для родного USB-порта
  }


  Serial.println("Goodnight moon!");

  // установите скорость передачи данных для порта SoftwareSerial
  mySerial.begin(2400);
}

void loop() { // запускайте снова и снова
  if (mySerial.read() == 0x10) {
    //mySerial.write(0x20);
    Serial.println("Data available");
    Serial.println(mySerial.read());
  }

  if (Serial.available()) {
    mySerial.write(Serial.read());
  }
  //delay(300);
}

И текущая схема (извините за мои навыки рисования)

, 👍5

Обсуждение

вы читаете только один байт после 0x10. это нормально? Если вы отправляете номера с последовательного монитора, они отправляются в виде текста. Я сомневаюсь, что счетчик принимает "10\r\n" (0x30, 0x31, 0x0D и 0x0A)., @Juraj

Джуст, у меня очень похожая проблема. Вы смогли заставить его работать? У меня недостаточно репутации, чтобы прокомментировать ваш пост;) поэтому я публикую свой запрос в качестве ответа здесь., @Atharva


2 ответа


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

4

Вы не показали, как подключили MAX3232, так что я ничего не могу сказать по этому поводу. Но я могу написать что-нибудь о вашем коде и о том, как данные поступают на компьютер.

  • На ПК: Здесь важно, как вы читаете серийные данные на ПК. Например, последовательный монитор ожидает текст в кодировке ASCII. Он не может корректно отображать двоичные данные. Вот почему вы получаете странных символов с правильным бодратом. Данные получены правильно, но они не представляют собой символы ASCII. Это просто двоичные данные, и интерпретация их как символов ASCII не будет иметь никакого смысла.

    Кроме того, он просто отправляет текст в кодировке ASCII, без двоичных данных. Поэтому, когда вы отправляете "10", он отправит байты 0x31 и 0x30, плюс любое окончание строки, которое вы выбрали (\n aka 0x0A, если вы выбрали newline в качестве окончания строки). Поскольку ваше устройство говорит в двоичных данных, вы можете захотеть говорить с ним в двоичном формате. Таким образом, вам нужно либо декодировать данные ASCII на Arduino, прежде чем отправлять их на устройство, либо использовать другую последовательную программу, которая может обрабатывать двоичные данные.

  • Ваш код: Сначала посмотрите на эту строку

      Serial.println(mySerial.read());
    

    Здесь вы читаете ровно один байт из интерфейса SoftwareSerial, а затем печатаете его. Serial.read() или SoftwareSerial.read() считывают только один байт из буфера, но данные с вашего устройства охватывают несколько байтов, как описано на странице wiki, на которую вы ссылаетесь. Посмотрите на таблицу под названием "Структура данных". Возвращаемые данные состоят в общей сложности из 10 байтов (пронумерованных от 0 до 9), где байты с 3 по 7 содержат фактические данные. Остальное дает данные конфигурации и состояния, заголовок в качестве начала пакета и контрольную сумму для проверки ошибок передачи. Поэтому чтения только одного байта недостаточно, вам нужно прочитать их все.

    Затем вы Serial.print()обрабатываете данные. Функция print() и ее братья и сестры форматируют данные, читаемые человеком. Это может помочь вам здесь, но вы должны убедиться, что полученный результат действительно читаем для вас, особенно когда вы читаете несколько байтов (как вам нужно сделать, как описано выше). Здесь нужно принять решение: Либо

    • вы можете декодировать и форматировать двоичные данные с устройства на Arduino и отправлять их в удобочитаемом формате по последовательному каналу.
    • или вы можете отправить двоичные данные прямо на компьютер и декодировать их там с помощью подходящей программы (либо последовательной программы, которая может обрабатывать двоичные данные, либо вашей собственной программы, например, с помощью python или processing).

    Если вы идете первым путем, вы должны сначала прочитать разные части сообщения, а затем распечатать эти части с дополнительным текстом, который объясняет, какие данные что означают. В противном случае у вас была бы просто длинная строка цифр, которые также довольно трудно прочитать. Кроме того, вы можете выбрать числовую базу. Я думаю, что здесь действительно важны только десятичные или шестнадцатеричные числа. Для этого вы можете использовать Serial.print(value, DEC) или Serial.print(value, HEX), который сообщает последовательной библиотеке использовать указанную базу чисел.

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

    Значение в кодировке BCD, один байт на цифру 0x00-0x09. 0x0a означает игнорируемую цифру. Последняя цифра представляет собой десятичную дробь.

    на связанном вики - сайте. Таким образом, хотя это все еще двоичные данные (не закодированные ASCII), каждый байт представляет собой десятичную цифру фактического значения. Вы можете расшифровать его соответствующим образом. Поэтому я предполагаю, что последовательность байтов 0x01 0x00 0x02 0x09 0x07 будет означать 1029.7. Поскольку вы написали, что уже знаете, как конвертировать данные, я не буду здесь углубляться в это.

    И, наконец, вам нужно переосмыслить структуру вашей программы. Рабочий процесс с вашим устройством выглядит следующим образом:

      Arduino waits for device to send data ready byte (0x10)
      device sends 0x10
      Arduino sends 0x20 (command to send data)
      Arduino waits for device to send at least 10 bytes of data
      device sends a complete data packet with 10 bytes
      Arduino interprets the data in the received packet and acts accordingly
      repeat
    

    Вы должны следить за этим рабочим процессом, а также предвидеть, что передача данных займет некоторое время (особенно при очень медленных 2400 бод). Для этого хорошо подходит Конечный автомат (FSM) (на этом сайте есть много расширенных описаний FSMS, например мой ответ на этот вопрос или этот вопрос, хотя есть еще много, которые вам просто нужно искать). Мы просто отслеживаем, в какой части рабочего процесса мы находимся в данный момент, и выполняем соответствующий код. Что-то вроде этого:

      enum ReceiveState {IDLE, RECEIVING};
      ReceiveState rec_state = IDLE;
    
      void loop(){
          if(mySerial.available()){
              switch(rec_state){
                  case IDLE:
                      if(mySerial.read() == 0x10){
                          // данные готовы, команда для отправки данных
                          mySerial.write(0x20);
                          // изменить состояние на получение
                          rec_state = RECEIVING;
                      }
                      break;
                  case RECEIVING:
                      if(mySerial.available() >= 10){// только чтение данных, если был получен полный пакет, не менее 10 байт
                          // Здесь вам нужно декодировать двоичные данные и обрабатывать их соответствующим образом (например, печать в нужном формате).
                          // Reset rec_state после этого, чтобы быть готовым к следующему разу, эти данные готовы к чтению
                          rec_state = IDLE;
                      }
                      break;
                  }
              }
          }
      }
    

    Это может показаться немного излишним, но концепция очень важна в мире Arduino, и она очень мощная. Кроме того, это облегчает размещение структур рабочего процесса.

,

Спасибо за подробное объяснение. Это поможет мне. Но у меня все еще есть одна проблема. Когда я добавляю строки печати отладки, они не будут напечатаны. Таким образом, никаких данных не поступает. Я думаю, что это связано с чипом MAX3232. Я добавил простую схему в главный пост. Может быть, он неправильно подключен или неправильный чип? Подключить RS232 напрямую к Arduino-это не вариант, если я правильно понимаю., @Joost


2

Привет и добро пожаловать в удивительный мир Arduino. Похоже,вы с легкостью подобрали Arduino. Ваша программа почти готова. Ваша схема подключения электропроводки выглядит правильно.

Когда я занимаюсь подобным проектом, я не буду сначала писать свое полное заявление. Вместо этого я пишу серию тестовых программ, чтобы убедиться, что каждый фрагмент работает именно так, как я хочу.

Я бы начал с написания программы, которая просто передает последовательную информацию, полученную от измерителя уровня звука, и отображает ее на последовательном мониторе. Что-то вроде этого:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()
{
  // Открыть последовательную связь USB
  Serial.begin( 2400 );
  Serial.println("USB Serial is up and running");

  // установите скорость передачи данных для порта SoftwareSerial
  mySerial.begin( 2400 );
}

void loop()
{ 
  if ( mySerial.available() )
  {
    // Запись серийного номера из S. L. M. в последовательный порт USB (debug) 
    Serial.write( mySerial.read() );
  }

  if ( Serial.available() )
  {
    // Запись серийного номера с последовательного порта USB (debug) на S. L. M.
    mySerial.write( Serial.read() );
  }
}

Этой программы должно быть достаточно для связи с измерителем уровня звука. Но это подводит меня к моей первой заботе - выбору последовательного монитора.

В то время как встроенный последовательный монитор Arduino адекватен, он действительно хорош только для отправки/получения текста. (Кроме того, он посылает CR/LF каждый раз, когда вы нажимаете enter.) Я сразу вижу, что вам нужно отправлять/получать шестнадцатеричные числа. Шестнадцатеричные числа плохо отображаются на последовательном мониторе.

Вместо этого я предпочитаю использовать терминальную программу "der-hammer" под названием hterm. (Скриншот здесь). Он позволяет просматривать входные данные в формате ASCII, hex, bin и decimal. Точно так же он позволяет отправлять ASCII, hex, bin и decimal или их смесь. При отправке информации вы можете настроить ее таким образом, чтобы она завершала данные либо подачей строки, либо возвратом каретки, либо обоими способами, либо вообще никакими.

Судя по всему, вам не нужны CR или LF в конце отправляемых вами данных. Поэтому в разделе "Параметры ввода" установите "Отправить при вводе" на "Нет".

Для полученных данных вам нужно просмотреть их в шестнадцатеричном формате, поэтому убедитесь, что флажок "шестнадцатеричный" установлен прямо над полученными данными. (Снимите флажок "ASCII".)

Вы можете создать тест обратной связи, удалив измеритель уровня звука и вместо этого связав RS232 Rx с RS232 Tx. Это позволит вам увидеть на hterm именно то, что видит измеритель уровня звука при отправке данных.


Проблемы с вашей программой

  if (mySerial.read() == 0x10) {
    //mySerial.write(0x20);
    Serial.println("Data available");
    Serial.println(mySerial.read());
  }

Раздел кода будет считывать только один байт данных после обнаружения 0x10 (см.Read() справочная страница). Все, что больше этого, будет потреблено в следующий раз, когда оператор if проверит последовательную строку на 0x10.

Я замечаю в строке Serial.println(mySerial.read()); что вы читаете данные (в шестнадцатеричном формате), но затем пытаетесь записать их в ASCII с помощью функции println (). Это не даст вам желаемого результата.

Кроме того, после обнаружения 0x10 он считывает последовательную строку программного обеспечения еще на один байт - это означает, что если после 0x10 ничего нет, то mySerial.Команда Read() будет висеть в течение 1 секунды до тех пор, пока не истечет время ожидания.

if (Serial.available()) {
  mySerial.write(Serial.read());
}

Как указывали другие, использование последовательного монитора будет отправлять это как символы ASCII (и включать символы CR/LF) вместо шестнадцатеричного значения, которое вы хотите. Вам воздается должное за то, что вы выбрали mySerial.write() вместо mySerial. println().


Мой взгляд на Вашу программу

Следующий код не проверен.

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()
{
  // Откройте последовательную связь и дождитесь открытия порта:
  Serial.begin(2400);
  // Ожидание подключения последовательного порта не требуется для Arduino Uno
  Serial.println("USB Serial is up and running");

  // установите скорость передачи данных для порта SoftwareSerial
  mySerial.begin(2400);
}

void loop()
{
  if ( mySerial.available() )
  {
    int recievedByte = mySerial.read();
    Serial.write(recievedByte);    // Send to USB serial - предполагая использование hterm в шестнадцатеричном формате
    if ( recievedByte == 0x10 )
    {
      mySerial.write(0x20);
    } 
  }

  // Вы можете удалить следующий код - данные должны быть в шестнадцатеричном формате.
  if ( Serial.available() )
  {
    mySerial.write(Serial.read());
  }
}
,

Большое вам спасибо за ваши усилия объяснить все это. Но, как уже было сказано ранее в другом посте, никаких данных не поступает. Может быть, что-то не так с MAX3232. При отладке (добавьте println в if-операторы) результата также нет. Только когда я отключил и снова подключил GND, я получаю данные из оператора mySerial.available() if., @Joost

О каком ВНД вы говорите?, @sa_leinad

Пока это работает. Я отсоединил микросхему MAX3232 и подключил устройство RS232 непосредственно к Arduino. Я знаю, что это не лучшая практика, но это низкое напряжение., @Joost