Отправлять одни и те же данные телеметрии на два разных последовательных порта?

Я использую Teensy 3.5 для управления роботом,следующим за стеной. Робот отправляет телеметрию на мой компьютер через два независимых порта - Serial1 и Serial. Serial-это обычный USB-порт, используемый при прямом подключении к роботу для устранения неполадок и т. Д. Serial1 привязан к приемопередатчику Bluetooth HC-05 и используется для удаленного получения телеметрии.

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

Serial.printf("\nChecking for MPU6050 IMU at I2C Addr 0x%x\n", MPU6050_I2C_ADDR);
Serial1.printf("\nChecking for MPU6050 IMU at I2C Addr 0x%x\n", MPU6050_I2C_ADDR);

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

Я попытался поиграть с strcpy и strcat без особого успеха:

  char quotestr[] = "\"";
  char hellostr[] = "hello";
  char instr[] = "%s\n, hellostr";
  char deststr[40];
  memset(deststr, 0, sizeof(deststr));
  strcat(deststr, quotestr);
  strcat(deststr, instr);
  strcat(deststr, quotestr);
  Serial.printf("quotestr = %s\n", quotestr);
  Serial.printf("instr = %s\n", instr);
  Serial.printf("deststr = %s\n", deststr);
  Serial.printf(deststr);

Но вывод неверен, потому что '\n' интерпретируется как пара CRLF, даже если она находится в строке с двойными кавычками.

quotestr = "
instr = %s
, hellostr
deststr = "%s
, hellostr"
"
, hellostr"

Кажется, должен быть способ сделать это легко, но я не могу понять. Любая помощь будет оценена по достоинству.

ТИА,

Фрэнк

, 👍-1

Обсуждение

\n интерпретируется как новая строка, как это обычно бывает для ОС на базе Linux. Я бы сказал, что он делает именно то, что вы ему велели. Где ты на это смотришь? Почему это проблема? А вы думали об использовании sprintf() для построения строки в массив char, который затем можно распечатать на обоих последовательных интерфейсах?, @chrisl

если вам не нужна новая строка, удалите \n из строки, @Juraj

Использование \\n вместо \n должно сделать свое дело., @Peter Paul Kiefer


2 ответа


4

вы можете написать простой вывод T, используя базовый класс Print. Стандартный класс печати Arduino не имеет printf, но из вашего кода похоже, что ядро Teense имеет printf в печати.

class TeePrint: public Print {

  Print &out1;
  Print &out2;

public:

  TeePrint(Print &_out1, Print &_out2) : out1(_out1), out2(_out2) {}

  virtual size_t write(uint8_t b) {
    out1.write(b);
    out2.write(b);
    return 1;
  }

};

TeePrint log(Serial, Serial1);

void setup() {
  Serial.begin(115200);
  Serial1.begin(9600);

  log.printf("abcd %d %s", 1234, "xyz");
}

void loop() {

}
,

Спасибо - ваш класс TeePrint очень элегантен. Одна незначительная гнида - он отказался компилироваться для T3.2, потому что " log " был объявлен в math.h. Когда я перешел с "log" на "myTee", все работало нормально, @user3765883


0

Самый простой метод - это вариационный макрос:

#define PRINTF(...) do { Serial.printf(__VA_ARGS__); Serial1.printf(__VA_ARGS__); } while(0)

Затем вы можете просто использовать PRINTF("foo: %d", bar); и он расширит его до двух printfs для вас.

,

Это приведет к сбою в if (bar) PRINTF("foo: %d", bar);. Вот почему люди используют идиому do {...} while (0)., @Edgar Bonet

@EdgarBonet Я подумывал о том, чтобы добавить " {}", но не стал утруждаться. Я должен был это сделать. Теперь я это сделал. Do и while не нужны., @Majenko

Do и while (0) необходимы для `if (bar) PRINTF(...); else ...- работать, как положено., @Edgar Bonet

@EdgarBonet Нет, вам нужно только {} для этого., @Majenko

Это неверно. Попробуй!, @Edgar Bonet

Разве это не будет отображать выходную строку дважды? Это может иметь огромное значение, если пользователь хочет печатать сложные вещи, особенно если речь идет о поплавках., @luni64

@EdgarBonet Я делаю это регулярно. Я никогда не использовал и даже не видел конструкцию do while., @Majenko

@luni64 да, это так, но на крошечном это не будет заметно, так как они намного быстрее, чем Arduino., @Majenko

Без do и while (0), if (bar) PRINTF(...); else ... дает’ошибку: "else" без предыдущего "if". Попробуй, серьезно! Конструкция do {...} while (0) является стандартным решением этой проблемы, и это [очень распространенная идиома](https://www.google.com/search?q=C+macro+do+while+0)., @Edgar Bonet

@EdgarBonet Ах, это будет тот факт, что вы добавили a ; после макроса, когда вы его использовали. Не делать этого?, @Majenko

Если вы не забудете _not_ добавить конечную точку с запятой после макроса, то это сработает. Но это сбивает с толку, так как макрос внешне выглядит как функция, но он должен использоваться с другим синтаксисом. Не могли бы вы воздержаться от добавления точки с запятой после getc (), потому что [это может быть реализовано как макрос](https://man7.org/linux/man-pages/man3/getc.3p.html)?, @Edgar Bonet

@EdgarBonet Тот факт, что все это написано заглавными буквами, свидетельствует о том, что это макрос. Такова условность. Конечно, если вы хотите добавить do while вокруг него, то do (while). Это не в моем стиле кодирования, так что я этого не делаю. Я пишу имена своих макросов заглавными буквами, чтобы помнить, что это макросы, и мне не нужно ; после них. Стиль кодирования каждого человека уникален., @Majenko

“Стиль кодирования каждого человека уникален” - это не оправдание того, что он сбивает с толку. Вы попали в свою собственную ловушку, так как случайно добавили точку с запятой в первых двух версиях этого ответа. Представьте себе, как легко кто-то другой, использующий определенный вами макрос, может попасть в эту ловушку., @Edgar Bonet

@EdgarBonet Если это заткнет тебе рот, я добавлю это. Хотя лично я им до сих пор не пользуюсь., @Majenko