Управление с телеметрией по serial

Добрый день!

Делаю шагающего робота на пульте управления. На роботе mega mini. В пульте pro micro с oled экраном, двумя аналоговыми стиками и энкодером. Связь через hc-12 на стандартных 9200 бод. Планирую отправку пакетов делать раз 100 в секунду (может пореже, не мозговал пока).

Привожу код того, что сейчас работает

pro micro в пульте формирует пакет с состоянием осей аналогового стика, кнопки и подобием crc

struct tCommunicationData {
  uint16_t x;
  uint16_t y;
  bool b;
  uint16_t crc;
};

tCommunicationData txData;

void setup () {

  Serial.begin(9600);
  Serial1.begin(9600);

  pinMode(15, INPUT_PULLUP);
  
}

void loop () {

  uint16_t x = constrain(analogRead(A1), 0, 1000);
  if (abs(x - 500) < 50) x = 500;
  uint16_t y = constrain(analogRead(A0), 0, 1000);
  if (abs(y - 500) < 50) y = 500;
  bool b = !digitalRead(15);

  Serial.println("Joy: x = " + String(x) + "; y = " + String(y) + "; b = " + String(b) + ";");

  txData.x = x;
  txData.y = y;
  txData.b = b;
  txData.crc = txData.b ? ~(txData.x ^ txData.y) : (txData.x ^ txData.y);
  Serial1.write((byte*)&txData, sizeof(txData));

  delay(100); // ясно дело ограничение количества отправок будет не с помощью delay()...

}

mega mini в роботе принимает пакеты, проверяет crc, выводит в usb-serial полученные данные

struct tCommunicationData {
  uint16_t x;
  uint16_t y;
  bool b;
  uint16_t crc;
};

tCommunicationData rxData;

void setup () {
  Serial.begin(9600);
  Serial2.begin(9600);
}

void loop () {
  if (Serial2.readBytes((byte*)&rxData, sizeof(rxData))) {

    uint16_t crc = rxData.b ? ~(rxData.x ^ rxData.y) : (rxData.x ^ rxData.y);

    if (crc == rxData.crc) {
      Serial.println("Joy: x = " + String(rxData.x) + "; y = " + String(rxData.y) + "; b = " + String(rxData.b) + ";");
    } else {
      Serial.println("corrupted data");
    }
  }

}

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

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

1 лайк

Интересные у вас какие стандарты…

Шо за бред я токашо прочитал?

1 лайк

если не подключать вывод set модуля hc-12 и не менять его конфигурацию, то он будет получать/принимать на 9600 (для него это настройка по умолчанию)

А теперь перечитай то, что я процитировал.

Сэр слышал о существовании такой науки как арифметика?

если ты хотел обратить внимание на опечатку “9200” вместо “9600” то следовало процитировать именно само число. Но в целом это на тему обсуждения никак существенно не влияет, да?
Вопрос в коде. Скорость передачи 9600, передаваемый с пульта пакет будет в 2.5 раза жирнее, чем сейчас. В обратку пульту полетят строчки, вернее скорее всего одна строка не более 80 символов, чтобы разбить по разделителю на 3-4 строчки и вывести на экран пульта. Может ли при использовании кода из топика случиться какой-либо неизлечимый рассинхрон, из-за которого продолжение получения корректных данных будет невозможно до перезагрузки устройств? Например из-за попытки чтения да завершения процесса получения пакета, или из-за переполнения очереди, т.к. плата в роботе будет занята тригонометрией и генерацией 18-ти PWM?

Именно так и произойдет.

то есть

Serial2.readBytes((byte*)&rxData, sizeof(rxData)

не будет дожидаться получения данных размерности sizeof(rxData) ?
Сударь, не знаете способов обхода этой неприятности?

Конечно знаю. Для этого придуманы стартовые байты, синхросигналы и т.п.

То есть продолжаем получать из serial, пока не получим кусок между двумя стартовыми байтами, и только тогда забираем данные между ними и парсим. Верно понял ваше предложение? Прием/передачу в таком случае лучше побайтно делать?

А с какой стати ему чего-то дожидаться?
Понимаете, МК делает ровно то, что ему сказал делать программист, и ничего от себя не придумывает.
Ну а способ стандартный: МК должен понять, когда заканчивается пакет.
Не из хрустального шара, а из структуры самого пакета.
Ключевое слово “протокол”.

Она в любом случае и так будет происходить побайтно.

И, кстати, Вы напрасно проигнорировали замечание из сообщения №6.

Я не увидел замечания в #6, в чем именно оно заключается? Последняя цитата относится к Serial0 (посмотреть результат на экране компа, в релизе этого не будет), а передача в прохе по Serial1, прием в меге по Serial2. Этот код вчера пару часов гонялся на столе, сбоев не вызвал, оси и кнопка передавались. Но мега разгружена, т.к. до тех пор пока я не реализую управление, чтобы в нем сделать калибровку крайних точек серво, я не буду проверять позиционирование конечностей, движение по параметрическим кривым 4й степени (хотя в openscad работает, там та же математика), и не смогу приступить к сочинению алгоритма совершения шагов… ну не суть.
Касаемо чтения побайтно, я думал раз размерность sizeof(rxData) передаю в write (пульт) и в readBytes (приемник) то это значение где-то используется во внутренней реализации Serial

Нет. UART физически работает с потоком байтов, ничем и ни как не связаных. Ловит каждый байт. А задача программы из этого потока взять нужные данные.
Про пост #6: 9600 бод это 1200 байт/с “сырых” битов. Если выкинуть старт-стоп биты, то останется меньше 1000 байт/с. Это несколько спорное решение, учитывая желаемые “100 пакетов в секунду”. В теории возможно, но как всегда “никогда не было, и вот опять!”

1 лайк

Мысль понял, в целом там пакет-то будет 8 бит на ось х4шт + 8 бит на все кнопки и направление вращения энкодера сразу + какая-нибудь контрольная сумма.
Вот в обратку строка… есть сомнения. Но в целом я 100 раз в секунду пальцами шевелить не могу, для незаметного в разрезе задачи импутлага думаю и 20 раз / сек достаточно.

Почему именно 9600? Почему бы выще не поставить? Не забывай, МК может “проспать” момент первого байта, и будет тупо ждать когда “весь поезд” проедет, чтобы начать смотреть сначала новый “поезд”.
Поэтому запас “на всякий” должен быть всегда. Тем более в коде до кучи “типа crc”, весьма “тяжёлую” по вычислениям. Я бы от неё отказался. UART и так на борту имеет проверку чётности.

1 лайк

Не знаю, как hc-12 переварит повышение скорости, его разработчики же чем-то руководствовались, выбрав именно такое умолчание. И если честно лень в его настройках ковыряться))) но мысль понял.
P.s. спасибо за беседу по существу

1 лайк

просто контрольку, как последовательный xor в коде в топике сделал, что-то легкое

не знал, спс

Может быть каким либо древним стандартом? Множество устройств типа электрощётчиков и прочих по умолчанию имеют 9600. Ардуина в примерах тоже всегда 9600, хотя отлично работает на 115200 .

3 лайка