Проблемы с памятью? dtostr() и strcat()
Я занимаюсь этим уже довольно давно и повсюду ищу ответы.. Я пытаюсь объединить два float в char*. Разделяются символом ",". Это код, с которым я работаю.
#include <DHT.h>
#include <RH_ASK.h>
#include <SPI.h>
#define DHT_pin 10
#define DHT_type DHT11
DHT dht(DHT_pin, DHT_type);
RH_ASK rf_driver;
float temp;
float hum;
char ch_temp[6];
char ch_hum[6];
char msg_out[12] = "";
void setup() {
Serial.begin (115200);
dht.begin();
if (rf_driver.init()) {
Serial.println("Radio transmitter initiated..");
}
else {
Serial.println("Radio transmitter failed to initiate..");
delay(1000);
}
}
void loop() {
temp = dht.readTemperature();
hum = dht.readHumidity();
dtostrf(temp, 5, 2, ch_temp);
dtostrf(hum, 5, 2, ch_hum);
strcat(msg_out, ch_temp);
strcat(msg_out, ",");
strcat(msg_out, ch_hum);
Serial.println(msg_out);
rf_driver.send((uint8_t *)msg_out, strlen(msg_out));
rf_driver.waitPacketSent();
Serial.print("data sent, package size: ");
Serial.println(strlen(msg_out));
delay(1000);
}
Я пробовал использовать разные размеры массивов символов, но это просто сводит с ума. Это вывод на консоль:
Radio transmitter initiated..
22.00,34.00
data sent, package size: 11
⸮⸮Radio transmitter initiated..
22.00,34.00
data sent, package size: 11
Таким образом, на своем пути через второй цикл он выходит из строя, перезапускается, а затем на своем третьем пути просто сдается... Что я делаю не так? :( Очень грустно из-за этого.
@Love.Berg, 👍2
3 ответа
Если вы удалите RF и код датчика, скетч, похоже, будет работать правильно при печати на последовательный монитор. При первом вызове rf_driver.send()
в цикле это работает. Во второй раз через цикл, и каждый раз после этого, он начинает печатать поврежденные данные. У меня нет окончательного ответа, почему это происходит, но у меня есть способ "исправить" это (или "взломать", если вы предпочитаете называть это так).
Использование memset()
для установки массива
символов msg_out во все 0 перед использованием, похоже, устраняет проблему. Вот тестовый скетч, который очень похож на ваш. Я использую некоторые жестко закодированные значения для данных датчиков, потому что у меня нет ни одного из этих датчиков.
#include <RH_ASK.h>
#include <SPI.h>
RH_ASK rf_driver;
float temp;
float hum;
char ch_temp[6];
char ch_hum[6];
char msg_out[12] = "";
void setup() {
Serial.begin (9600);
if (rf_driver.init()) {
Serial.println("Radio transmitter initiated..");
}
else {
Serial.println("Radio transmitter failed to initiate..");
delay(1000);
}
}
void loop() {
// Установите содержимое буфера на все 0
memset(msg_out, 0, sizeof(msg_out));
temp = 12.345;
hum = 67.890;
dtostrf(temp, 5, 2, ch_temp);
dtostrf(hum, 5, 2, ch_hum);
strcat(msg_out, ch_temp);
strcat(msg_out, ",");
strcat(msg_out, ch_hum);
Serial.println(msg_out);
rf_driver.send((uint8_t *)msg_out, strlen(msg_out));
rf_driver.waitPacketSent();
Serial.print("data sent, package size: ");
Serial.println(strlen(msg_out));
delay(1000);
}
Ваш скетч выполняется на основе заимствованной памяти.
Каждый раз, когда выполняется функция цикла, новый текст объединяется с strcat()
, и буфер msg_out
никогда не сбрасывается.
В первый раз используется 12 байт, во второй раз 23 байта, в третий раз 34 и так далее.
Решение в ответе @VE7JRO исправит это, но я предпочитаю не использовать dtostrf()
и передавать двоичные данные.
Я вижу и несколько других проблем:
- Почему вы хотите передавать читаемый текст в формате ASCII? Используйте двоичные данные. Либо числа с плавающей запятой, либо целые числа. Например, целое число в 1/100 градусов.
- Библиотека RadioHead использует много памяти. Вы не можете использовать другую библиотеку, которая также использует много памяти.
- Значение '5' для dtostrf - это минимальная ширина, поэтому вам лучше сделать эти массивы, например, 20 байтами. Я предпочитаю временно помещать эти массивы в стек.
- DHT11 не является точным. Если вы перейдете на более качественный датчик с помощью I2C, то вам потребуется больше библиотек (библиотека проводов и библиотека датчиков), и у вас может закончиться память sram.
- При использовании библиотеки RadioHead в режиме по умолчанию используются три контакта (rxPin =11, txPin = 12, pttPin =10). Вам лучше добавить раздел комментариев к своему скетчу, чтобы объяснить, что вам не следует использовать эти контакты. У вас уже есть конфликт на выводе 10 для датчика DHT и pttPin.
В старой и больше не поддерживаемой библиотеке VirtualWire есть некоторые ошибки, но она использует меньше памяти, чем библиотека RadioHead. Библиотека RadioHead не оптимизирована для плат Arduino, таких как Uno, Nano, Leonardo и других. Для этого даже требуется #включить <SPI.h>
, потому что это часто используется с приемопередатчиками, даже если шина SPI не используется в режиме RH_ASK (спасибо @VE7JRO за объяснение этого в комментарии ниже).
Самый простой способ передать более одного значения - это использовать массив.
float myData[2];
myData[0] = 23.456;
myData[1] = 68.0;
rf_driver.send((uint8_t *)myData, sizeof(myData));
Sizeof()
не является реальной функцией, она сообщает компилятору заполнить размер этой переменной. Поскольку число с плавающей
запятой равно 4 байтам, вы также можете ввести число 8 для второго параметра.
Существует несколько решений для приемника. Мне нравится использовать указатель на буфер, но, возможно, проще скопировать их в переменные с помощью memcpy()
.
float myData[2];
if (driver.recv(buf, &buflen)) {
if (buflen == sizeof(myData) { // дополнительная проверка
memcpy((uint_8 *)myData, buf, sizeof(myData));
Serial.println(myData[0]);
Serial.println(myData[1]);
}
}
Когда приемник также является платой Arduino, тогда проблем быть не должно. Когда получателем является что-то другое, вам, возможно, придется проверить, совпадает ли порядок байтов. Переменная с плавающей
запятой Arduino на 100% совместима с IEEE, но иногда проще использовать только целые числа.
Наиболее распространенным способом является использование структуры
. Структура
- это пакет, который может содержать все виды данных (целые числа, байты, массивы и так далее). Конечно, получатель и отправитель должны иметь точно такое же определение для структуры
.
Я опаздываю на вечеринку, но вот что происходит:
msg_out[]
является глобальным, но это не обязательно. Переместите его определение и инициализацию в loop() , единственную функцию, которая его использует. Затем он инициализируется при каждом входе в loop(), а не только один раз при инициализации программы. Нет необходимости очищать массив; просто обнуление нулевого элемента инициализирует его для использования в виде символьной строки, и это то, что теперь делает ваш инициализатор (но только один раз).
- Нужна помощь с библиотекой U8GLIB
- Помогите уменьшить размер скетча!
- Serial.println использует слишком много памяти (не строки)
- Стирание 1 байта внешней Flash памяти (winbond)
- Почему Serial.print(1) требует на 228 байт больше программной памяти по сравнению с Serial.print((char)(48+1))?
- Отладка максимальной емкости
- deserializeJson() не удалось: NoMemory при отправке последовательного json с использованием ArduinoJson
- avrdude ser_open() can't set com-state
Возможно, операционная система не использует SPI. Если вы удалите включаемый элемент из скетча, он не будет компилироваться. Существует скетч под названием ask_transmitter.pde, который поставляется вместе с библиотекой. Рядом с SPI include есть комментарий, в котором говорится: "Фактически не используется, но необходим для компиляции". Я новичок в Arduino, поэтому я не понимаю этого комментария. Если ни один из файлов RadioHead не использует какие-либо функции и т.д. в библиотеке SPI, почему он не будет компилироваться без нее? Я поддерживаю вашу рекомендацию избавиться от датчика "низкого качества", который использует операционная система. За 1,28 доллара США вы можете приобрести приличный датчик Bosch I2C, такой как BMP280, по цене Aliexpress.com ., @VE7JRO
@VE7JRO верно, я знал это, но забыл об этом., @Jot
Спасибо вам за этот обстоятельный ответ! По-настоящему оцените это!, @Love.Berg
@Jot Спасибо вам за этот подробный ответ! По-настоящему оцените это!! Причина, по которой я конвертирую в Char *, заключается в том, чтобы собрать его вместе, чтобы я мог отправить его в одном пакете, а затем позже другой микроконтроллер легко разобрал его и отобразил отдельно. Как бы я собрал поплавки вместе и смог бы разделить их позже? Я пробовал VirtualWire, но в данном случае это все, что должен делать этот MCU. Но я буду иметь это в виду в будущем! Спасибо! Этот проект предназначен для школьного задания, но я заметил, что DHT11 не очень надежен.. Спасибо, что указали на конфликт выводов!!, @Love.Berg
@Любовь.Berg Я добавил дополнительный раздел для передачи массива. Он передается как двоичные данные и принимается как двоичные данные, поэтому все, что вам нужно сделать, это взять двоичные данные из переменных с плавающей запятой, передать их и в приемнике поместить двоичные данные обратно в переменные с плавающей запятой., @Jot