Отправка дробного числа в VirtualWire.h по 433Mhz

sashadeg
Offline
Зарегистрирован: 02.03.2016

Здравствуйте.

Возникла проблемма, при отправке дробного числа передатчиком с помощью библиотеки VirtualWire. Если на передатчике переменной number назначить дробное число, то  приемник возвращает в переменную number какое то безобразное пятизначное число. При отправке целого числа, все работает правильно.

Я понимаю, что дело с какими-то кодировками и назначении типа переменной, но пока в этом соображаю плохо. Что можно с этим сделать? 

Собирал проект по этой статье

Передатчик:

// Тестировалось на Arduino IDE 1.0.1
#include <VirtualWire.h>

void setup(void)
{
  vw_set_ptt_inverted(true); // Необходимо для DR3100
  vw_setup(2000); // Устанавливаем скорость передачи (бит/с)
}

void loop(void)
{
  int number = 123;
  char symbol = 'c';
  
  String strMsg = "z ";
  strMsg += symbol;
  strMsg += " ";
  strMsg += number;
  strMsg += " ";
  
  char msg[255];
  
  strMsg.toCharArray(msg, 255);
  
  Serial.println(msg);
  
  vw_send((uint8_t *)msg, strlen(msg));
  vw_wait_tx(); // Ждем пока передача будет окончена
  delay(200);
}

Приемник:

// Тестировалось на Arduino IDE 1.0.1
#include <VirtualWire.h>

void setup()
{
  Serial.begin(9600);
  vw_set_ptt_inverted(true); // Необходимо для DR3100
  vw_setup(2000); // Задаем скорость приема
  vw_rx_start(); // Начинаем мониторинг эфира
}

void loop()
{
  uint8_t buf[VW_MAX_MESSAGE_LEN]; // Буфер для сообщения
  uint8_t buflen = VW_MAX_MESSAGE_LEN; // Длина буфера

  if (vw_get_message(buf, &buflen)) // Если принято сообщение
  {
    // Начинаем разбор
    int i;
    // Если сообщение адресовано не нам, выходим
    if (buf[0] != 'z')
    {
      return; 
    }
    char command = buf[2]; // Команда находится на индексе 2
    
    // Числовой параметр начинается с индекса 4
    i = 4; 
    int number = 0;
    // Поскольку передача идет посимвольно, то нужно преобразовать набор символов в число
    while (buf[i] != ' ')
    {
      number *= 10;
      number += buf[i] - '0';
      i++;
    }
    Serial.print(command);
    Serial.print(" ");
    Serial.println(number);
  }
}

 

Araris
Offline
Зарегистрирован: 09.11.2012

sashadeg пишет:

Я понимаю, что дело с какими-то кодировками и назначении типа переменной, но пока в этом соображаю плохо. 

Правильно понимаете. А отчего бы Вам не подразобраться с типами данных ? Например, отсюда начать : http://arduino.ru/Reference

oleg_kazakof
Offline
Зарегистрирован: 24.04.2015

Умножте число на 10, 100 или на 1000 и дроби не будет после приёма сделайте обратное действие.

 

sashadeg
Offline
Зарегистрирован: 02.03.2016

Я пока добился, чтобы код работал и передавал данные. Но не совесем понимаю как это происходит, а с типом переменных мне вообще сложно. 

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

 

sashadeg
Offline
Зарегистрирован: 02.03.2016

oleg_kazakof пишет:

Умножте число на 10, 100 или на 1000 и дроби не будет после приёма сделайте обратное действие.

Спасибо! Действительно решило задачу очень просто. Но это все равно "костыль", хоть и очень простой и гениальный =) .

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Ну, если Вы не чувствуете в себе силы Джедая, чтобы написать свой преобразователь из строки в число, то запишите все в буфер и прочтите из него форматным вводом sscanf(). Даже переставлять байты в строке руками не обязательно, для этого есть strrev(). Так устроит, не "костыль"?

По памяти, ессно, неэффективно, но не зато требует скила. ;-)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:

Ну, если Вы не чувствуете в себе силы Джедая, чтобы написать свой преобразователь из строки в число, то запишите все в буфер и прочтите из него форматным вводом sscanf(). Даже переставлять байты в строке руками не обязательно, для этого есть strrev(). Так устроит, не "костыль"?

По памяти, ессно, неэффективно, но не зато требует скила. ;-)

Есть одна беда - AVR-Lib не поддерживает плавающую точку в scanf / printf

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

sashadeg,

позвольте поинтересоваться, а зачем вообще Вы предобразуете своё число в текст, чтобы потом, на другой стороне преобразовывать обратно?

Не проще ли передавать его прямо как есть, без всяких преобразований? И принимать соответсвенно готовым?

sashadeg
Offline
Зарегистрирован: 02.03.2016

wdrakula пишет:

Ну, если Вы не чувствуете в себе силы Джедая, чтобы написать свой преобразователь из строки в число, то запишите все в буфер и прочтите из него форматным вводом sscanf(). Даже переставлять байты в строке руками не обязательно, для этого есть strrev(). Так устроит, не "костыль"?

По памяти, ессно, неэффективно, но не зато требует скила. ;-)

пока не понял, что вы написали, но разберусь =) Теперь примерно представили мой скилл в програмировании, да? =)

ЕвгенийП пишет:

sashadeg,

позвольте поинтересоваться, а зачем вообще Вы предобразуете своё число в текст, чтобы потом, на другой стороне преобразовывать обратно?

Не проще ли передавать его прямо как есть, без всяких преобразований? И принимать соответсвенно готовым?

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

Цитата:
Во первых мы формируем строку strMsg. Используем тип String, т.к. с ним проще работать (можно конкатенировать его с числами, используя оператор '+').

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

Далее передадим символ 'c', который может означать выполнение какой-то команды и число 123, как параметр к этой команде. На практике удобно работать именно таким образом.

После этого преобразовываем тип String к стандартному массиву символов при помощи метода toCharArray и передаем его в команду vw_send.

Наш код будет отправлять строку 'z c 123'.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

Есть одна беда - AVR-Lib не поддерживает плавающую точку в scanf / printf

Грешен! Не подумал.

Пришлось закрывать пробел. Оказалось, что библиотека libscanf_flt.a с которой можно собрать в IDE, содержит плавающую точку только для vfscanf(), но не для sscanf(). Вот так. То есть можно, конечно пересобрать sscanf и подключить к IDE в platform.local.txt. Но это на порядок сложнее, что просто написать с нуля.

Долго копался, но эрудиция лишней не бывает. Спасибо, Евгений! Искренне. Люблю в деталях покопаться.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula,

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

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Евгений! Я же не об этом. Мне пока не требовалось преобразование и я не догадался бы искать функцию, если нет в avr-gcc, ну и фмг с ней, проще написать.

Я старался запустить обычный scanf(), указав библиотеку, как в документации по  avr-gcc описано.

Разобрался, что в библиотеке только vfscanf() из всего семейства, о чем и сообщил.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Я понял, что Вы просто экспериментировали. Просто на всякий случай сказал по ту функцию, вдруг не знаете, а пригодится.