Как отправлять и получать беззнаковые целые (unsigned int) от одного arduino к другому arduino

Я пытаюсь отправить несколько больших чисел в диапазоне от 0 до 62000 с одного Arduino на другой через последовательную связь. Я использую unsigned int для хранения значений от 0 до 65535. И я использую serial.print() и serial.write() для отправки чисел и serial.read()-'0' ; для получения номеров, но я не могу отправлять и получать номера выше 9. Пожалуйста , помогите мне.

Вот мой код -

(Для мастера)

const int Button_1 = 2;
unsigned int s =0;
void setup () 
{   
   Serial.begin(115200) ;
   pinMode(Button_1,INPUT);
} 
void loop() 
{
  if (digitalRead(Button_1))
  {
     s = 61805;
     Serial.write(highByte(s)) ;
     Serial.write(lowByte(s));
     delay (5);
     s=0;
  } 
  else
  {
     s = 60005;
     Serial.write(highByte(s)) ;
     Serial.write(lowByte(s));
     delay (5);
     s=0;
  } 
} 

(код для подчиненного устройства)

unsigned int upperByte, lowerByte, r=0,p=0;
void setup() 
{
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT) ;
} 
void loop () 
{
  If (Serial.available()>1)
  {
     upperByte = Serial.read();
     lowerByte = Serial.read();
  }
    r = (upperByte << 8) + lowerByte ;

   while (r==61805)
    {
        digitalWrite(LED_BUILTIN, HIGH) ;
    } 
    while (r==60005)
    {
        digitalWrite(LED_BUILTIN, LOW) ;
    } 
} 

, 👍2

Обсуждение

подсказка: с помощью Serial.write(b) вы получите 256. с двумя Serial.write(b) вы получите 256 * 256 (65536), @Juraj

Если вы хотите сделать это в ASCII (в отличие от байтовых значений, как предложил Юрай), вам следует использовать разделитель сообщений — специальный символ, который отмечает конец числа. Для этого вы можете использовать символ новой строки \n. Затем вы можете читать цифры одну за другой, складывая их в большое число. Для дальнейшей помощи нам нужно увидеть ваш код., @chrisl

Использование двух серийных.write (b) — хорошая идея, но с помощью этой логики я не могу получить много чисел в диапазоне от 0 до 65536. И мой код очень простой, я просто использую Serial.write() для отправки и последовательного доступа. read(), чтобы получить... Не думаю, что это вам поможет., @Mayank

Что вы имеете в виду: «Я не могу получить много чисел в диапазоне от 0 до 65536 с помощью этой логики»? Вы можете отправить столько целых чисел, сколько захотите. Вы должны отправить каждое целое число в виде пары байтов, как показано в ответе CrossRoads. Каждое целое число без знака может содержать значение от 0 до 65535., @Duncan C

я могу переместить до 32 байтов с одного Arduino на другой, используя архитектуру «главный/подчиненный». если это поможет, я могу опубликовать ответ, @tony gil


2 ответа


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

3

Есть 2 способа передачи данных по последовательному порту: в виде байтовых данных или в кодах ASCII

  1. Байтовые данные: Все, что хранится или обрабатывается в цифровом виде, представляет собой серию нулей и единиц (битов) (электрически ВЫСОКИЙ и НИЗКИЙ уровень), которые сгруппированы в байты (8 биты). Таким образом, все данные — это просто числа на низком уровне. Различные типы данных (такие как числа, строки, исполняемый код) достигаются только путем различной интерпретации байтовых данных. Вы можете напрямую взять числовое значение байта. Например, такой байт, как 0b00000101, эквивалентен десятичному значению 5. Таким образом, каждый байт может содержать числа от 0 до 255. (Погуглите двоичную систему для получения дополнительной информации). Чтобы хранить большие числа, объединяется больше байтов. Например, целое число состоит из 2 байтов (16 бит) и может содержать числа от 0 до 65535. Примечание. Это беззнаковые типы. Существуют также знаковые типы, которые также могут содержать отрицательные значения. Чтобы понять это, погуглите двоичное дополнение до двух. Чтобы передать такой большой тип по последовательному каналу (который основан на байтах), вам нужно разделить байты типа, отправить их последовательно и собрать их вместе на принимающей стороне. Вы можете сделать это с помощью следующего кода:

    unsigned int number = 54321;
    Serial.write(number & 0xFF); //получение младшего байта числа с помощью побитового И
    Serial.write(number >> 8); // получение старшего байта числа путем сдвига битов значения на 8 бит вправо
    

    Для Arduino определены макросы, которые могут сделать это за вас:

    Serial.write(lowByte(number));
    Serial.write(highByte(number));
    

    В долгосрочной перспективе вам следует изучить, как работают побитовые операторы. Они часто нужны. Чтобы снова собрать данные, вы можете посмотреть ответ CrossRoads.

    Примечание: Serial.write() отправляет байтовые данные без изменений, а Serial.print() перед отправкой преобразует данные в ASCII. Они не равны.

  2. Данные ASCII. Одной из интерпретаций байтовых данных является стандарт ASCII (который действительно устарел). Каждое значение байта от 0 до 127 получает соответствующее значение ASCII, означающее символы алфавита, цифры, некоторые специальные символы и некоторые управляющие символы. Числа, которые передаются как данные ASCII, состоят из нескольких байтов, по одному на каждую цифру. Это то, что вы пробовали. Поскольку получатель должен разделить разные числа, чтобы правильно их обработать, вы должны отметить конец каждого числа. Обычно это делается с помощью символа новой строки \n (возможно, ему предшествует символ возврата каретки \r, стиль Windows). Вы добавляете это в конце каждого числа. Serial имеет для этого дополнительную функцию: Serial.println() напечатает параметр как ASCII и добавит возврат каретки и символ новой строки. На принимающей стороне вы читаете байты в буфер, пока не увидите эту последовательность разделителей. После этого вы обрабатываете весь буфер. Если вы хотите узнать, как эта последовательная связь реализована в коде, посмотрите на пример SerialEvent, поставляемый с Arduino IDE.

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


EDIT: я описал выше, что при использовании данных ASCII вы можете использовать один или несколько специальных символов для разделения одного сообщения/номера. Затем получатель может различать числа в этом ряду цифр. Это просто какой-то протокол, который вы навязываете данным. Вы утверждаете, что каждое сообщение заканчивается этим специальным символом, так же как предложение в английском языке заканчивается точкой.

Важным требованием является то, чтобы специальный символ не встречался внутри сообщения/номера. Поэтому нельзя использовать символ '1' в качестве разделителя, так как он также будет встречаться внутри ваших чисел, и вы неправильно нарежете числа.

Тот же принцип можно применить в качестве протокола к потоку байтов. Например: Вы определяете, что каждое сообщение/число будет заканчиваться специальной последовательностью байтов, чтобы получатель всегда мог сказать: "Это все данные числа, а не только его часть". Как и выше, специальная последовательность байтов не должна встречаться внутри допустимых данных. Вы написали, что ваши числа всегда находятся в диапазоне от 0 до 62000. Это означает, что значение 65535 никогда не будет встречаться как допустимые данные, поэтому вы можете использовать его как разделитель. В шестнадцатеричном формате 65535 записывается как 0xFFFF, что означает, что вы отправляете два байта, каждый бит которых равен 1 (каждый байт имеет десятичное значение 255). В вашем коде каждый раз, когда вы что-то получаете, вы можете проверить, есть ли у вас 2 последовательных байта со значением 255 (или записано как 0xFF). Если нет, запишите данные в буфер. Если да, это означает, что вы можете преобразовать данные внутри буфера в число.

Для отправки это будет выглядеть так:

Serial.write(lowByte(number));
Serial.write(highByte(number));
Serial.write(0xFF);
Serial.write(0xFF);

Примечание. Этот протокол гарантирует, что отправитель и получатель не смогут рассинхронизироваться. Но все же первое чтение после проблемы с передачей (например, когда байт был отправлен до того, как получатель был готов) будет мусором. Это случается очень-очень редко, поэтому для большинства людей это не проблема. Есть и другие принципы, которые вы можете использовать в своем протоколе для смягчения этих проблем, если хотите. Например, вы можете определить другую специальную последовательность байтов в качестве начала сообщения/номера. Или вы можете рассчитать контрольную сумму номера и использовать только те сообщения/номера, которые соответствуют этой контрольной сумме.

,

Ой ок, я думаю это точно поможет (:, @Mayank

Я опубликовал свой код, извините, он не организован, потому что я использую его на своем телефоне, поэтому не вижу возможности его организовать. Он не работает, я пытаюсь использовать базовую программу для отправки и получения этих высоких значений. цифры.. Пожалуйста, проверьте это, ты (:, @Mayank

Логика не работает не знаю в чем проблема, помогите пожалуйста решить, @Mayank

@Mayank Как это не работает? Какое поведение вы на самом деле видите в этом коде? Используете ли вы внешний понижающий резистор для кнопки? И код получателя среагирует только один раз, поскольку он застрянет, как только будет получено одно из двух значений для r, потому что в циклах while r не может измениться. Вместо while используйте для тестирования оператор if., @chrisl

Обратите внимание, что при двоичной передаче, если получатель пропустит первый байт (возможно, загрузка займет больше времени, чем отправитель), он потеряет синхронизацию. Он соединит второй байт первого числа с первым байтом второго числа и получит фиктивный результат. Выходы из этой ситуации есть (контрольные суммы, разделители кадров + escape-код...), но они не так просты. ASCII намного проще, и его следует отдавать предпочтение, если пропускная способность не является проблемой., @Edgar Bonet

Даааа, я использую внешний понижающий резистор для кнопки. Если хочешь, я попробую внутреннее сопротивление? О, ок, я внесу эти изменения и посмотрю (:, @Mayank

Способ ASCII намного лучше, чем байтовый? Не знаю как им пользоваться, буду рад научиться (:, @Mayank

На самом деле сервопривод застрял на 0° и не движется., @Mayank

О, так мне нужно ставить \n и \r после каждого числа в моем предыдущем коде? Будет ли работать нормально?, @Mayank

Да, после каждого номера. Получатель должен различать цифры, поэтому вам понадобится разделитель. Сервопривод? Ваш код не использует сервопривод. Передача байтов не так хороша. Это зависит от того, что вы хотите отправить. Цифры еще в порядке. Вы можете смягчить рассинхронизацию из-за проблем с запуском, немного подождав перед отправкой данных. Вы также можете наложить некоторый протокол на поток байтов. Например, вы можете выбрать короткую последовательность байтов, которая не будет встречаться в ваших данных (например, 0xFFFF), которая может обозначать конец числа. Тогда вы не сможете рассинхронизироваться., @chrisl

Если вы хотите использовать ASCII, посмотрите пример SerialEvent в Arduino IDE. Это показывает, как это часто делается., @chrisl

Ты так много!! ((: после этих изменений все заработало.. Ох? Где я поставил это 0×FFFF.. Не могли бы вы объяснить это на небольшом примере кода?, @Mayank


1

Попробуйте что-нибудь подобное для отправки и получения/рекомбинации двух байтов:

Сторона отправки:

Serial.write (highByte (your unsigned int));
Serial.write (lowByte (your unsigned int));

Принимающая сторона:

if (Serial.available() >1){ // получено два байта
upperByte = Serial.read();
lowerByte = Serial.read();
}
newInt = (upperByte <<8) + lowerByte;
,

Могу ли я получить на выходе 257? С такой логикой?, @Mayank

257 десятичное = 0x0101. старший байт = 0x01, младший байт = 0x01. x01 << 8 + 0x01 = 257 десятичных, так что обязательно., @CrossRoads

Да, вы можете использовать этот метод для отправки любого значения от 0 до 65535 (2^16 - 1). Если вам нужны значения большего размера, вы можете отправить беззнаковый длинный размер, используя 4 байта. Это даст вам **огромный** диапазон значений. (от 0 до 2^32-1, что превышает 4 миллиарда.), @Duncan C

О, круто! Тай ((:, @Mayank

Что такое верхний байт и нижний байт? Тип данных типа Int?, @Mayank

байты. Я объявляю их перед setup(), чтобы они были глобальными. Я объявляю там все свои переменные. Вероятно, это неправильное использование области видимости переменных, но для небольших встроенных программ, которые я создаю, это работает нормально., @CrossRoads

О, понял... ты! (:, @Mayank