Проговаривание float.
- Войдите на сайт для отправки комментариев
Делаю сигнализацию. Хочу в принципе избавится от СМС. Только дозвон или емайл оповещение.
Если со вторым все понятно, то с первым затык.
В чем проблема?
Вот к примеру прочитал я данные с DHT22, или вычлинил цифры из запроса баланса СИМ-карты, запомнил в переменные типа float. Дальше - звонит "начальника" и посылает DTMF 0 = получить инфу о состоянии системы.
И тут я затупил. Вот как мне разбить float по разряду, типа сотые, десятые, потом единицы, десятки, сотни, тысячи, и т.д, дабы проиграть, соответствующие разряду файлы, на микрофонный вход модема?
Может проект такой есть где часы вешают, библиотека? Чет никак слова не подберу для поисковика, чтобы пример глянуть. Не подскажет ли кто?
http://netcode.ru/cpp/?artID=247
Вот код из файла Print.cpp среды Ардуино.
То, что надо! Спасибо большое.
Вот что-то такое получилось. (я там Serial.print() использую вместо проигрывания файлов, чисто для отладки)
а вот это вывовд в монитор порта
Вроде все корректно.
Может кто умный глянет? Может как-то его оптимизировать, код этот, а то больно много памяти жрет - 12%. А мне ещ и все остальное писать, а тут только эта функция столько ОЗУ сожрала.
Вот чувствую - быдлокод это, а сам уже лучше не сделаю. Учусь только. ((
ПС. Забыл добавить, функция обрабатывает только четыре разряда + сотые, то-есть до +-9999.99. Я так думаю больше денег на балансе сим-карты вряд-ли кто держать будет.
А зачем ему знать прям до сотых копеек? Давно делали что-то подобное, так просто по команде запрос баланса проговаривалось 3и сообщения: "Баланс отрицательный", "Баланс меньше 100 рублей", "Баланс больше 100 рублей". Когда меньше 100 докладывали, а если больше ста так и ничего не надо делать. С влажностью тоже по началу прям точно выводили, а потом заказчик сказал "заколебался я слушать" и сделали тоже несколько сообщений типа "влажность ниже нормы", "влажность в норме", "влажность выше нормы". Так интуитивно понятнее чем слышать корявым голосом цифры и потом соображать ага 55% это сойдет.
Так я решил сразу на все случаи жизни сделать.
Эта функция, в принципе что хочешь проговорить сможет, те же часы к примеру. Замени "целые" на "часов", а "сотые" на "минут", и вот, - уже часы с кукушкой. ))
kostyamat. по моему вы сами усложняете себе и м.к. задачу. Нужно отказаться от float вообще. Поправить библу dht, что б отдавала результат измерения в двух байтах. И все дела.
kostyamat. по моему вы сами усложняете себе и м.к. задачу. Нужно отказаться от float вообще. Поправить библу dht, что б отдавала результат измерения в двух байтах. И все дела.
совсем без библиотеки - можно сказать ничего в памяти не ест
Спасибо, конечно. Но у меня влажность только как пример. И к сути не относится. Да и не заметил я, чтобы DHT библиотека так уж ОЗУ жрала. Да и суть вопроса не в DHT.
а.... виноват, не к тому датчику бросил прогу это для DS18B20, извиняюсь.
У меня вот так в лоб влажность проигрывалась
Можно спокойно добавить и до 9999 и больше. Если надо именно float то разбиваете его на два целых числа как-нибудь вот так
ну это как один из вариантов и дальше проигрываете как два числа и между ними проигрываете "celyh.wav"
Очень интересное, иэффективное решение. Но вот в чем странность, ваш пример потребляет 5% ОЗУ, но если сделать вот так
byte celoe = (int)balans;
byte drobnoe = (balans - celoe) * 100;
playValue(celoe);
playValue(drobnoe);
или так
int celoe = balans;
byte drobnoe = (balans - celoe) * 100;
playValue(celoe);
playValue(drobnoe);
без разницы, то потребление ОЗУ резко растет до АЖ! 25%
Мой же пример, используя PROGMEM удалось урезонить до 9%.
Ваш пример
"Скетч использует 2040 байт (6%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 520 байт (25%) динамической памяти, оставляя 1528 байт для локальных переменных. Максимум: 2048 байт.
Мой пример
Скетч использует 5224 байт (17%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 198 байт (9%) динамической памяти, оставляя 1850 байт для локальных переменных. Максимум: 2048 байт.
Мой забирает под себя 17% флеш, против ваших 6%, но он и читает сразу весь флоат, и сразу универсальнее. К тому же подозреваю, что потребление флеш растет из-за использования класса String, котрый линкуется, при компиляции. То есть, исходя из того , что String я все равно использую в основной программе, функция, в реале , не должна увеличивать потребление флеш так уж критично.
Но вот что занятно. Почему повторное использование вашей функции резко подымает потребление ОЗУ? Понять не могу.
Конечный вариант моего примера
Если Вас волнует расход ресурсов, а судя по сообщениям, то так, то навсегда забудте String и почти навсегда float. Например для передачи указателя на строку в PROGMEM используйте PGM_P, обявляем void SendAT_P( PGM_P at){ вызываем SendAT_P(PSTR("abc")); Вместо float пользуйте формат с фиксированой точкой и т.д.
Извините, нубский вопрос: SendAT_P(PSTR("abc")); и SendAT_P(F("abc"));
разве не равнозначно?
И второй вопрос: используя PROGMEM мы выиграли в ОЗУ но потеряли флеш? Нет?. Хотя, логика подсказывает, флеш мы теряем в любом случае, судя по всему.
1. Нет. F дает __FlashStringHelper* В принципе тоже иногда пользую, он удобней для вывода в Serial, и менее удобен в остальных случаях. Но если только про расположение в PROGMEM разговор - то оба расположат строку там одинаково.
2. В ОЗУ выигрываем т.к. строка не копируется в него, а в флеше она будет в обоих случаях. Если на мелочи не смотреть (нужен код для извлечения из флеша, извлекая из флеша надо кудато расположит, и т.д.) То выигрываем в ОЗУ и не теряем в флеше.
Мой же пример, используя PROGMEM удалось урезонить до 9%.
Ну если мягкое с теплым сравнивать, то конечно оно будет как попало. Если в тот мой кусочек кода добавить PROGMEM то оперативнки будет занимать 188 байт в независимости от того сколько раз вызывается. Тот кусок просто показывал, что можно без String'а обойтись, тут его ой как не любят ) Да и если есть возможность обойтись без него, лучше так и сделать.
2. Я так и думал.
String мне, как начинающему, все равно использовать придется. В классе String очень много удобных, и главное понятных, инструментов для работы с текстовыми данными. Аналоги которых, в случае char, придется фактически писать самому. И я правильно понимаю? Если я уж использую String один раз где-то в программе, то класс String линкуеться, при сборке, один раз, и повторное его использование к увеличению объема компилированого кода не приводит. Тяжело мне пока с char.
Penni, упаси Б-г, если Вы подумали, что я критикую ваш код. Я просто многого пока не понимаю. Но стараюсь. И да, мне ваш пример показался очень эффектным, с точки зрения простоты кода. Думаю и работает он раз в сто быстрее моего. Что не мало важно. Вот только прикол с ОЗУ я не понял.
Поблема не в String как механизм, проблема у том что вовремя не освобождаются от лишних переменных когда они уже не нужны. Вот у вас есть объем пространства. Вам нужно что то там сделать. Вы занесли туда иструмент и сделали. Но инструмент надо вынести из этого пространства. И тогда объем сохранится. Но если постоянно заносить инструменты, то ваше простанство будет забито ими. Да кончно все под рукой. Но рабочий объем станет маленьким и вы не сможете ремонтировать более объемные вещи. Это основная причина почему идет рост объема ОЗУ. Необходимо работать с большими вещами и держать еще в этом объеме много инструментов, которыми давно на деле уже не пользуются. Но облом выбрасывать.
Вас шантажируют или угрожают? ;)
Проблема тут как с наркотиками, начнете пользовать - тяжело отказатся. А наличие его сильно ограничит возможности проекта. Получается так, человек подсядет на String, пишет простенькие проги, все вроде хороше, растет опыт, нарабатывает себе готовый код с стрингами. Постепенно проекты усложняются и начинает нехватать ресурса. И получается вилка: либо 1.переходить на более мощный процессор либо 2.переучиватся и переписывать код более эффективным. Ну еще и третий вариант - забить на все и пойти на пиво:) Как правило 1 таки приводит к 2. т.к. с совместимостю все гладко только поверхам, тот же PROGMEM как правило отсутствует впринципе. Вобщем выстраданій совет - лучше с плохим не начинать . Темболее альтернатива вот http://cppstudio.com/cat/309/325/ и эффективно и универсально (работает последние 30 лет на любом проце!) и не так уж сложно.
Поблема не в String как механизм....
угу. плюс еще способствует фрагментации кучи, когда и место вроде есть, а программа падает. Причем падает давно работающая и вроде 100 раз проверенная. И раз в час (или день или месяц или год - как повезет). Просто так обстоятельства сложились, так события наложились, память выделялась и освобождалась так, что на миллионном проходе вдруг нет целого блока свободной памяти для очень нужного. И все, зависли. Хороше если хоть есть куда хоть шото вывести о ошибке (тогда мы видим на экране торгового автомата какието коды например). А иногда и нет даже такой возможности (при ошибке в модуле экрана на экран не выведеш ;). Это одна из специфик встроенных систем. Такую ошибку даже отделу профи пофиксить - задача (логер не допишеш, места нет, небольшое вмешательство в код - ошибка то уйдет, то вернется, тестируй днями, есть она или нет).
Особо все плохо от того что такие проблемы - в конце проекта и при эксплуатации, когда позно уже все переписывать.
Logik. Разумеется так и есть. Программы чаще достраиваются а не пишутся сверху вниз. Разумеется так выгоднее и дешевле. Сначало постоили один сарай, потом постоили еще и еще. И в конце сараев много , а подьехать не получится, потому что подъезды застоены сараями. Теперь надо выносить все на хр**н. И строить многоэтажный производственный центр. ПС: Считайте что String это стандартный каркасный сарай. И если вам хочется небоскреб, то надо рисовать новый проект элементов для строительства этого небоскреба. А char строки , по той-же анологии, это только кирпич
к которому надо изучить методы кладки еще.
Мы все тут чуток в прострацию ударились. А у новичков, типа меня, с char огромнейшие проблемы. Вот к примеру, даже в примере вверху, я долго не мог заставить char принимать цифру как символ, а не код символа, час рылся в инете пока не нашел реализацию:
_fname[i] = '0' + toPrint;
В другой программе, которую сейчас доделываю, мне нужно разом очистить char str[60] типа как
String str = "";
решения я пока так и не нашел
Делать в коде двести раз
for (int i = 0; i <= 59, i++) {
str[i] = ' ';
}
считаю бредом сивой кобылы, да и повторение этой конструкции не улучшает положение с размером в ОЗУ и флеш. (Хотя подозреваю, что после компилятора эта конструкция занимает во флеш меньше чем String str = "";). Но все равно, пахнет быдлокодом. И писать это 100500 раз в программе - увольте, даже копи/пастом. Наверное есть же простое решение?
Проблема в том, что ардуино занимаются миллионы, а документировано, именно такое элементарное, очень плохо.
Новичкам нужны примеры простых вещей, и желательно без умных слов "живёт на стеке", "указатель на переменную" и т.п.
Вот я к примеру, "указатель на переменную" в лоб так и не понимаю, разве что где-то на интуитивном уровне, но так чтобы уверенно взять и пользоваться - нет. И не помещается с разгону все в 45-ти летнюю голову.
Ещё одна проблема, может кто подскажет: подстановка переменной как макрос (вот даже не знаю как это правильно назвать) в функцию (несколько раз упростило бы жизнь, обошел другими путями), типа
char parm = "\"AT\", \"OK\", 1000"
SendAT($parm);
В бытность мою (до 28 лет) программистом FoxPro, там такая штука была. Это есть даже в bash shell на линукс.
В Arduino Wirning, C# это реализуемо?
Выше Вам давали ссылку на функции для работы со строками, вот там есть memset
memset(str, ' ', 60); полностью заменит весь цыкл.
Имеется ввиду передача парметра функции? Передаете указатель на строку и все.
"Передаете указатель на строку и все"
Що! Апять? Я же писал что не понимаю сути "указатель на..."
Можно просто пример такой передачи? Может догоню, наконец. ((
Дык почитайте https://habrahabr.ru/post/256443/
Если что не поймете, конкретно спросите.
Вот у вас есть мобильник. У мобильника есть номер. С точки зрения мобильника номер мобильника это его имя. А вот вы являетесь переменой. Потому что мобильник может оказаться у другого. Если кто-то позвонит вам и скажет передайте трубку вашей , к примеру, жене. Это идет присвоение. Но если вы напишете СМС позвоните по этому номеру, то эта СМС-ка и есть указатель на ваш номер.
Указатель - просто адрес в памяти. Если пишем типа char s[]="abc", то s - указатель (адрес) где располагается первый символ строки, его можно вывести как число и посмотреть Serial.print((int)s); можна использовать как число и менять. Если пишем *s или s[0] - получим первый символ строки, *(s+1) или s[1] или char *r=s+1; *r - второй символ и т.д. Можна менять значения в строке s[4]='D' и строка станет "abD", можна а s[4] - байт 0 (не символ '0') признак конца строки. Если сделать s=s+1 то теперь строка s ,будет "bc", можна вернуть обратно s=s-1. Можна математику с символами строки делать s[0]=s[0]+3; и первая буква станет не 'а' а 'd' (по используемой таблице символов смотрим). Строка - просто массив символов завершающаяся нулем. Найдите функцию вывода в сириал дампа области памяти (не найдете я выложу, у меня дето валяется), выведете дамп строки до и после своих опытов - за 5 минут поймете что куда. Там все примитивно просто. К тому же прикольно сочитается с теми функциями что выше было например, memset(str, ' ', 60); заполнит пробелами первуе 60 символов, а memset(str+4, ' ', 10); оставит как есть 4 первых символа и очистит 10 следующих а str[5]=str[4] сделает шестой символ таким же как пятый.
Есть такой момент.
Только совершенно не там, где Вы его видите.
Вот представьте, у Вас спрашивают, как проехать туда-то. Вы начинаете отвечать, но Вас переспрашивают, сколько раз нужно нажать на педаль газа и сколько раз - на педаль торможа, и в какие моменты времени это нужно делать.
К чему это я? А к тому, что в нормальном случае тот, кто спрашивает дорогу, долже уже уметь водить машину, а тот, кто дорогу объясняет, вправе рассчитывать на такое умение у спрашивающего.
В Ардуино используется стандартный язык программирования. И искать сведения, касающиеся языка, в документации по Ардуино все равно, что требовать у того, кто показывает дорогу, научить Вас водить автомобиль.
А вот документация на язык программирования (в отличие от документации на Ардуино) как раз имеется в больших количествах и высоком качестве.
Вот, вариант совершенно без использования класса String
Смею заметить, но писать такое совершенно не удобно, а вот это все http://cppstudio.com/cat/309/325/ ведет себя иногда совершенно неадекватно.
К примеру вот это
Serial.println(strcat("abc", _fname);
через раз приводит не к выводу ожидаемой строки, а к безостановочному выводу мусора в консоль, да так, что аж комп подвисает.
Странно то, что раз работает корректно, а иногда, между if ---- else, начинается вот такое вот.
А вот это int8_t int_part_length = strlen(int_part) возвращает 45, хотя int_part = 1234, то-есть 4 знакоместа. Чтобы получить корректное значение нужно сначало int перевести в char, тогда получишь корректное значение. То-есть сначало получить длинну, а потом задать размер char под этот int в лоб не получится. Нужно использовать промежуточную переменную. В котрой мерять длинну, а потом переписывать ее уже в нужную, создав ее по длинне строки с интом. Фатал! ((
кстати,
"Скетч использует 3366 байт (10%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 244 байт (11%) динамической памяти, оставляя 1804 байт для локальных переменных. Максимум: 2048 байт."
Пример со String кушал ОЗУ меньше, правда флеша почти в два раза больше. Но опять же из-за линковки класса. А так, как линковать к программе мне его все равно придется, то метод String получается єкономичнее. Разве что вообще табу на String в программе сделать, тогда линковаться не будет. Но и выигрыш флеша смешной в ущерб удобству.
Все там нормально работает! Посмотрите что возвращает strcat. Указатель на "первый параметр", т.е. подразумевается что выделено достаточно памяти под объединение строк, а у Вас первым параметром передается "abc"... По той ссылке даже пример использования дан который будет прекрасно работать.
По поводу strlen так на то она и называется strlen, почитайте описание там написано, что она возвращает длину строки по нулевому символу (\0). Вы передали указатель на тип инт вот она и пошла по памяти пока не нашла \0 поэтому и вернула 45.
qwone объяснял почему String не используют.
По мне Ваша задача решается вообще без использования каких-либо строк, будть то String либо char* (ну кроме тех что в progmem будут лежать с именами проигрываемых файлов).
Ну и, например, если ввести 30.00 что должна выдать программа? Поидее 30 целых 0 сотых процента а что выведет? 30, 0, целых, процента.
Мда, забыл проверку по 0 сотых сделать. Кстати, а как названия файлов правильно в пPROGMEM положить. А то F(), не работает. Если передавать функции значения char* file через вот так PSTR("celych.wav"), то в выводе в сериал каша из символов, вместо названий файлов. При этом компилятор молчит шо партизан. Никаких ошибок. Why?
К примеру вот это
Serial.println(strcat("abc", _fname);
через раз приводит не к выводу ожидаемой строки, а к безостановочному выводу мусора в консоль, да так, что аж комп подвисает.
Ничего удивительного после строки:
В Си-строке обязан быть завершающий ноль, а у Вас вместо 0 - '0', что далеко не одно и то же.
Пример со String кушал ОЗУ меньше, правда флеша почти в два раза больше.
Это Вам так кажется.
String опасна тем, что кушает динамическую память, а Вы опираетесь на статистику по использованию статической.
Переделал немного
убрал ошибку вывода 0000.wav, плюс добавил обработку разно-звучание "одна гривня, две гривни, пять гривен". Мне под українську мову работает отлично, под русский рубль надо еще чуток доделывать. В украинском "одна гривня - одна ціла", в русском "один рубль НО одна целая" - этого функция не учитывает. Еще решил не передавать в нее заглавие и скажем "процентов". А вызывать перед ней и после, так универсальнее. Так можно и баланс СИМки читать, и температуру, и влажность, давление и т.п.
ПС. Значения больше 9999.99 не озвучиваются, проигрывается файл "оверДофига" и на выход. ))
В связи с обсуждением отказа от String вопрос - как Arduino IDE относится к malloc, realloc и т.п?
В принципе, прошелся поиском, нормально относится :))
Вопрос снят ...
Привет! Код агонь! А можно листинг какие цифры надо озвучивать? как вы их генерировали? не в микрофон же бубнеть?
Если не умеете работать с микрофоном или стесняетесь своего голоса, можно найти программу с массой заготовок, например, автомобильного навигатора, и из нее надергать файлы с нужными числительными.
Звуковые файли создавал вот этой программой https://play.google.com/store/apps/details?id=hesoft.T2S
Выбираете нужный язык генерации в настройках и она вам генерит введённый текст в waw формат. Особенно мне в ней украинский язык понравился. Очень голос приятный, и сочное такое произношение. :)
Файлы по формату 0000.waw.
1 = 0001.waw, 90 = соответственно 0090.waw.
Файлы нужны для таких рядов цифр 0-20, потом десятки 30 - 90 (0030.waw, 0040.waw и и.д), аналогично сотни 100-900.
minus.waw для минусовых чисел, over10t.waw - что то типа "число вне диапазона".
Вот не помню как там тысячи. Ну введите в функцию что-то типа 2057.67 и посмотрите что она выводит.