RSS блога
Подписка
Учим разговаривать DIY с помощью MP3 модуля
- Цена: $1.30
- Перейти в магазин
Когда мы мастерим различные полезности для дома, используя Ардуино, мы мало задумываемся, как получать информацию от этого устройства. Если ее немного, то обходимся чащe всего светодиодами. Но если информации выводить нужно больше, то конечно, это будет видео в том или ином виде — разнообразные индикаторы и дисплейчики, коих в продаже великое множество. Ибо это просто и это привычно. Да, зрение у нас основной орган чувств, но иногда звук гораздо более удобный, а зачастую и единственный способ донести до нас информацию. Примерами могут служить будильник, сигналка на авто или, увы, слабовидящие люди. Но в своих поделках дальше использования пищалки мы звуком не пользовались, поскольку не было удобного инструмента для этого. Теперь это в прошлом.
В этом обзоре мы научим разговаривать (и даже немного шутить) часы и другие приборы и напишем аудиосинтезатор, с помощью которого Ардуино сможет произносить любые числа из натурального ряда голосом.
Для начала о цене вопроса. Не слишком ли она окажется высока для заявленных возможностей? И вот тут нас ожидает первый и весьма приятный сюрприз. Чуть больше доллара MP3 модуль плюс чуть меньше бакса micro SD карточка на 64 mb (я брал на ebay по 99 центов), без которой модуль бесполезен. Недорого, правда? Да, для вывода звука, конечно, понадобится еще динамик, но поскольку никаких выдающихся характеристик от него не требуется, мы его покупать не станем, а вытащим, к примеру, из какого-нибудь старого системника или радиоприемника. А зато в качестве бонуса мы получим возможность выбросить из проекта пищалку, если она там была — с ее ролью прекрасно справится сам MP3 модуль.
Познакомимся с железом:
Модуль представляет собой вполне себе самостоятельный девайс, который можно использовать и без Ардуино. Для этого достаточно подключить к нему несколько кнопок (или даже резистивную клаву), динамик или внешний усилитель, вставить micro SD карточку с музоном и все, можно слушать любимые композиции. Но это совсем неинтересно. Гораздо интересней управлять им программно, чем мы сегодня и займемся. Ниже расписаны все его контакты, но нам для работы с Ардуино понадобятся только первые семь.
Вывод Описание
1. VCC Питание «+»
2. GND Питание «−»
3. RX UART приём
4. TX UART передача
5. SPK1 Громкоговоритель «+»
6. SPK2 Громкоговоритель «−»
7. BUSY Индикатор состояния («0» — простой, «1» — проигрывание)
8. DAC_R Выход на наушник или усилитель (канал «R»)
9. DAC_L Выход на наушник или усилитель (канал «L»)
10. IO1 Вход управления: короткое нажатие — «назад», длинное — уменьшить громкость
11. IO2 Вход управления: короткое нажатие — «вперёд», длинное — увеличить громкость
12. ADKEY1 Порт для подключения резистивной клавиатуры, вход 1
13. ADKEY2 Порт для подключения резистивной клавиатуры, вход 2
14. USB+ USB порт, вывод «+»
15. USB− USB порт, вывод «−»
Что нам следует знать о железке? Плеер может проигрывать как mp3 файлы с битрейтом до 320kbps, так и wav. На его борту присутствует встроенный усилитель, к которому, согласно даташита, можно подключить динамик и выжать до 3W. Качество звука не ахти, но для экспериментов пойдет. Лучше использовать все-таки внешний стерео усилитель, задействовав выводы DAC_R и DAC_L. Для питания требуется напряжение в диапазоне 3.2 — 5V. В плеере присутствует простенький эквалайзер на 5 профилей: NORMAL, POP, ROCK, JAZZ, CLASSIC и BASS. Общаться наш плеер с Ардуино будет через последовательный интерфейс с скоростью 9600bps. В принципе, используя команды из даташита, можно управлять плеером напрямую через UART, но мы будем использовать библиотеку mp3TF, где управление реализовано нагляднее и удобнее. Для обратной связи будем использовать пин BUSY, чтобы знать о состоянии плеера (режим воспроизведения или ожидания). Один интересный момент касается реализации связи по UART. Хотя, в даташите указано прямое соединение TX-RX, RX-TX, народ рекомендует включить в эти цепи по резистору 1-2 кОм. Без них, якобы, появляются искажения, идет нагрев плеера и у кого-то даже плеер вышел из строя. Поверим и включим эти резисторы.
О железе плеера пока все, можно приступать к сборке. В качестве Ардуино я буду использовать Uno, но подойдет любая модель. Поскольку плеер с динамиком 0.5W потребляет прилично для возможностей USB, с которого и пойдет у нас питание, на высокой громкости довольно существенно просаживается напряжение, из-за чего плеер вырубается. Поэтому я к цепи питания добавил конденсатор на 4700 mkF. После такой модификации плеер стабильно работает на любой громкости.
Теперь что касается программной части. Как я уже упоминал выше, я буду использовать библиотеку mp3TF, а не более распространенную DFPlayer-Mini-mp3, так как эта библиотека, предоставляя бОльшие возможности, генерит более компактный код. Но самое главное, в ее составе есть функции, позволяющие воспроизводить файлы из нужной папки! Присутствуют также стандартные функции управления громкостью, навигацией, эквалайзером и несколько экзотических, которые мы применим для модернизации Тетриса.
Немного об организации файлов на micro SD карте при использовании функции playFolder2 из этой библиотеки. Файлы могут располагаться в папках с двузначным номером. Допустимо использовать не более 15 папок, в каждой из которых до 3000 файлов. Файлы при этом сортируются по имени в алфавитном порядке. Номер трека указывается в соответствии с этой сортировкой. Чтобы не путаться, перед именем файла будем ставить четырехзначный номер, типа 0015file.mp3 или просто 0015.mp3.
Чтобы изучение возможностей плеера было нескучным, соберем на макетной плате несколько полезных устройств из того что у меня оказалось под рукой.
Поскольку были найдены модуль реального времени DS1307 и дисплейчик на TM1637, то соберем говорящие часы. Активатором голосового вывода времени будет сенсорный датчик.
Подготовка micro SD карточки. Отформатируем ее в FAT16 или FAT32 и наговорим в микрофон все возможные часы и минуты. Получится 84 файла — 24 часа и 60 минут.
Скачаем и установим библиотеки DS1307RTC.h и TimeLib.h для работы с часами, TM1637Display.h для работы с дисплеем и SoftwareSerial.h для организации виртуального последовательного порта. Можно, конечно, пользоваться и встроенным портом, но так мы получим более гибкую конструкцию с одинаковыми номерами пинов для подключения всех моделей Ардуино. Собираем схему. Теперь нам нужно установить время в наших часах. Поскольку мы собираем часы только для демонстрации возможностей плеера, то не будем писать код для установки и корректировки даты/времени, а воспользуемся примером из состава библиотеки DS1307RTC.h с названием SetTime. Этот скетч просто заливает в RTC дату/время своей компиляции, он выполнит инициализацию модуля DS1307 и больше нам не потребуется. Теперь зальем в папку с именем 02 на флешку наши голосовые файлы, набросаем небольшой скетч для наших часов и запустим его.
Мы увидим на дисплее текущее время и моргающее двоеточие-секунды. А теперь прикоснемся к сенсору и вот оно! Часы сообщают нам время голосом. Такие часы я видел когда-то очень давно и мне очень хотелось тогда их иметь, но, увы, возможностей не было. Теперь, не особо напрягаясь, любой может собрать их за несколько минут. Но мы пойдем еще дальше и дадим часам сварливый характер. Пусть наши часы начнут ворчать, если к сенсору прикасаться слишком часто.
Для этого добавим несколько файлов с соответствующей озвучкой и несколько строк кода. Обновленный скетч будет выглядеть так:
А вот как они работают.
Теперь, если касаться сенсора чаще, чем раз в 5 сек, часы начинают раздражаться, а после 3-го раза вообще обижаются и перестают сообщать время. Но, конечно, ненадолго ). После 5 секунд настроение у них снова восстанавливается. Характеры можно придумать разные, все ограничивается только нашей фантазией. Собрав подобные часы в корпусе (мыльница?), мы получим отличный подарок для близких. И даже весьма полезный для слабовидящих. А может кому то понравится возможность узнавать ночью время, не открывая глаз, а просто коснувшись корпуса часов рядом. Себе-то я точно соберу, как только приедет микрофон (чтобы в зависимости от шумности вокруг, регулировалась громкость).
Собирая часы, мы использовали полный набор файлов с часами и минутами, где в каждом файле присутствует число и его размерность: “Два часа”, “Двадцать одна минута”, “Восемнадцать часов” и т.д. Нам не пришлось заниматься лексическим разбором: “одна минутА”, но “две минутЫ” или “пять минуТ”. Мы упростили себе жизнь, соединяя только 2 файла – количество часов и количество минут. А комбинаций всех возможных часов и минут получилось всего 84, это немного и нас это устроило. Но мы можем озвучить не только часы, но и показания любых других приборов, значения которых могут превышать тысячи и более. В этом случае все возможные комбинации не надиктуешь. Значит нужно написать универсальный голосовой синтезатор чисел. Этим и займемся.
Как мы произносим число? Есть базовые лексические единицы и сборные. Например, числа “один”, “пятнадцать”, “семьдесят”, “сто” являются базовыми, а число “сто двадцать пять” синтезируется из 3-х базовых единиц. В интервале от 0 до 19 числа все базовые, поэтому надиктуем их в 20 файлов. Далее идут десятки от 20 до 90, их также запишем в 8 файлов. За ними 9 файлов с произношением сотен. Дальше начинаются тысячи. Тут уже новый лексический нюанс. “ОДНА тысячА”, “ДВЕ тысячИ” и “пять тысяЧ”. Почти все в тысячах будет соответствовать и для более высоких размерностей, кроме слов “ОДНА” и “ДВЕ”. Для всех остальных размерностей это будут слова “ОДИН” и “ДВА”. Надиктуем эти варианты произношения для тысяч в 2 файла и добавим их в нашу папку. А затем по три варианта произношения для каждой последующей размерности: «миллион», “миллиона”, “миллионов” и т.д.
Все файлы надиктованы, идея понятна, осталось воплотить её в скетч. Но мы-же программисты, ёпт…
Вот такая небольшая функция voicedig() способна лексически верно произнести любое натуральное число до 42-х разрядов. А вы сможете? Тогда вперед! )))
Видео работы голосового синтезатора:
Если кому-то этого недостаточно, можно надиктовать еще размерностей, но мне их названия неизвестны. (Не проблема обучить систему произношению дробей и еще многому чему, но для демонстрации возможностей сабжа вполне, думаю, приведенного примера будет достаточно.)
Теперь давайте применим нашу функцию для реальных измерений. Для этого добавим на макетку датчик освещенности BH1750. Значит, нам понадобятся еще 3 файла со словами “люкс”, “люкса”, “люксов”. Закинем эти файлы в новую папку и напишем скетч для опроса датчика. Немножко похулиганим и добавим в инициализацию прибора прибора приветствие ).
Видео работы говорящего люксметра:
Заменим люксметр на дальномер, поправим немного код и получим говорящий дальномер.
Видео работы говорящего дальномера:
Конечно, данные датчики используют нашу функцию лишь на малую долю. Но ее возможностей хватит на любой прибор с запасом.
Напоследок мы модернизируем игру Тетрис из этого обзора. Отключим уже ненужную пьезопищалку и поставим сабж. Запишем фоновый трек, звук проваливания линии и озвучим установление нового рекорда. Применим из вышеозвученной библиотеки крайне интересные функции: repeatCurrent(bool start) (Проигрывать по кругу текущий трек) и setInterCut(uint16_t track)(ставит текущий проигрываемый трек на паузу и проигрывает заданный).
Посмотреть, что вышло можно ниже. Прошу прощение за качество записи, особенно звука. Снимал чем было, но главное, я думаю, оценить можно. Кстати, из видео можно услышать звук проваливания линий. Кто первый догадается откуда он, сразу плюс в карму! ))
Все скетчи, описанные выше, работают с тем набором файлом, под который заточены. Скачать этот набор можно здесь. Разумеется, я не диктор, потому рекомендую тем, кто захочет повторить эти эксперименты, попросить человека с хорошей дикцией надиктовать эти файлы. Главное — соблюдать последовательность.
Итак, мы увидели, что звук может не только заменять дисплеи, но и, дополняя их, заметно разнообразить выдачу информации. Здесь все будет зависеть только от нашей фантазии… и умения немного программировать.
Выводы:
Из минусов я, пожалуй, отмечу только ограничения на имена файлов и папок, поскольку нужно знать не столько имя файла, сколько его место в таблице FAT. Из-за этого, бывало, надиктуешь полсотни файлов с именами 0001-0050, а один в середине пропустишь. И потом приходится вручную переименовывать кучу файлов, чтобы вставить забытый. Поэтому я написал себе программку на Delphi для вставки и удаления файлов в папке, так, что теперь это для меня не проблема, но производителю все-таки нужно подумать над этим.
Плеер обладает огромным потенциалом и, при своей низкой цене и творческом подходе способен стать отличным помощником программиста микроконтроллеров.
Вердикт: Must have!
В этом обзоре мы научим разговаривать (и даже немного шутить) часы и другие приборы и напишем аудиосинтезатор, с помощью которого Ардуино сможет произносить любые числа из натурального ряда голосом.
Для начала о цене вопроса. Не слишком ли она окажется высока для заявленных возможностей? И вот тут нас ожидает первый и весьма приятный сюрприз. Чуть больше доллара MP3 модуль плюс чуть меньше бакса micro SD карточка на 64 mb (я брал на ebay по 99 центов), без которой модуль бесполезен. Недорого, правда? Да, для вывода звука, конечно, понадобится еще динамик, но поскольку никаких выдающихся характеристик от него не требуется, мы его покупать не станем, а вытащим, к примеру, из какого-нибудь старого системника или радиоприемника. А зато в качестве бонуса мы получим возможность выбросить из проекта пищалку, если она там была — с ее ролью прекрасно справится сам MP3 модуль.
Познакомимся с железом:
Модуль представляет собой вполне себе самостоятельный девайс, который можно использовать и без Ардуино. Для этого достаточно подключить к нему несколько кнопок (или даже резистивную клаву), динамик или внешний усилитель, вставить micro SD карточку с музоном и все, можно слушать любимые композиции. Но это совсем неинтересно. Гораздо интересней управлять им программно, чем мы сегодня и займемся. Ниже расписаны все его контакты, но нам для работы с Ардуино понадобятся только первые семь.
Вывод Описание
1. VCC Питание «+»
2. GND Питание «−»
3. RX UART приём
4. TX UART передача
5. SPK1 Громкоговоритель «+»
6. SPK2 Громкоговоритель «−»
7. BUSY Индикатор состояния («0» — простой, «1» — проигрывание)
8. DAC_R Выход на наушник или усилитель (канал «R»)
9. DAC_L Выход на наушник или усилитель (канал «L»)
10. IO1 Вход управления: короткое нажатие — «назад», длинное — уменьшить громкость
11. IO2 Вход управления: короткое нажатие — «вперёд», длинное — увеличить громкость
12. ADKEY1 Порт для подключения резистивной клавиатуры, вход 1
13. ADKEY2 Порт для подключения резистивной клавиатуры, вход 2
14. USB+ USB порт, вывод «+»
15. USB− USB порт, вывод «−»
Что нам следует знать о железке? Плеер может проигрывать как mp3 файлы с битрейтом до 320kbps, так и wav. На его борту присутствует встроенный усилитель, к которому, согласно даташита, можно подключить динамик и выжать до 3W. Качество звука не ахти, но для экспериментов пойдет. Лучше использовать все-таки внешний стерео усилитель, задействовав выводы DAC_R и DAC_L. Для питания требуется напряжение в диапазоне 3.2 — 5V. В плеере присутствует простенький эквалайзер на 5 профилей: NORMAL, POP, ROCK, JAZZ, CLASSIC и BASS. Общаться наш плеер с Ардуино будет через последовательный интерфейс с скоростью 9600bps. В принципе, используя команды из даташита, можно управлять плеером напрямую через UART, но мы будем использовать библиотеку mp3TF, где управление реализовано нагляднее и удобнее. Для обратной связи будем использовать пин BUSY, чтобы знать о состоянии плеера (режим воспроизведения или ожидания). Один интересный момент касается реализации связи по UART. Хотя, в даташите указано прямое соединение TX-RX, RX-TX, народ рекомендует включить в эти цепи по резистору 1-2 кОм. Без них, якобы, появляются искажения, идет нагрев плеера и у кого-то даже плеер вышел из строя. Поверим и включим эти резисторы.
О железе плеера пока все, можно приступать к сборке. В качестве Ардуино я буду использовать Uno, но подойдет любая модель. Поскольку плеер с динамиком 0.5W потребляет прилично для возможностей USB, с которого и пойдет у нас питание, на высокой громкости довольно существенно просаживается напряжение, из-за чего плеер вырубается. Поэтому я к цепи питания добавил конденсатор на 4700 mkF. После такой модификации плеер стабильно работает на любой громкости.
Теперь что касается программной части. Как я уже упоминал выше, я буду использовать библиотеку mp3TF, а не более распространенную DFPlayer-Mini-mp3, так как эта библиотека, предоставляя бОльшие возможности, генерит более компактный код. Но самое главное, в ее составе есть функции, позволяющие воспроизводить файлы из нужной папки! Присутствуют также стандартные функции управления громкостью, навигацией, эквалайзером и несколько экзотических, которые мы применим для модернизации Тетриса.
Немного об организации файлов на micro SD карте при использовании функции playFolder2 из этой библиотеки. Файлы могут располагаться в папках с двузначным номером. Допустимо использовать не более 15 папок, в каждой из которых до 3000 файлов. Файлы при этом сортируются по имени в алфавитном порядке. Номер трека указывается в соответствии с этой сортировкой. Чтобы не путаться, перед именем файла будем ставить четырехзначный номер, типа 0015file.mp3 или просто 0015.mp3.
Чтобы изучение возможностей плеера было нескучным, соберем на макетной плате несколько полезных устройств из того что у меня оказалось под рукой.
Поскольку были найдены модуль реального времени DS1307 и дисплейчик на TM1637, то соберем говорящие часы. Активатором голосового вывода времени будет сенсорный датчик.
Подготовка micro SD карточки. Отформатируем ее в FAT16 или FAT32 и наговорим в микрофон все возможные часы и минуты. Получится 84 файла — 24 часа и 60 минут.
Скачаем и установим библиотеки DS1307RTC.h и TimeLib.h для работы с часами, TM1637Display.h для работы с дисплеем и SoftwareSerial.h для организации виртуального последовательного порта. Можно, конечно, пользоваться и встроенным портом, но так мы получим более гибкую конструкцию с одинаковыми номерами пинов для подключения всех моделей Ардуино. Собираем схему. Теперь нам нужно установить время в наших часах. Поскольку мы собираем часы только для демонстрации возможностей плеера, то не будем писать код для установки и корректировки даты/времени, а воспользуемся примером из состава библиотеки DS1307RTC.h с названием SetTime. Этот скетч просто заливает в RTC дату/время своей компиляции, он выполнит инициализацию модуля DS1307 и больше нам не потребуется. Теперь зальем в папку с именем 02 на флешку наши голосовые файлы, набросаем небольшой скетч для наших часов и запустим его.
Говорящие часы
Таблица соединений:
Сенсор сигнал — Pin 10
Mp3 модуль RX — Pin 8
Mp3 модуль TX — Pin 7
Mp3 модуль Busy — Pin 9
TM1637 CLK — Pin 2
TM1637 DIO — Pin 3
DS1307 Sda — Pin 4
DS1307 Scl — Pin 5
Питание везде 5V. Папка 02 на micro SD обязательна.
Сенсор сигнал — Pin 10
Mp3 модуль RX — Pin 8
Mp3 модуль TX — Pin 7
Mp3 модуль Busy — Pin 9
TM1637 CLK — Pin 2
TM1637 DIO — Pin 3
DS1307 Sda — Pin 4
DS1307 Scl — Pin 5
Питание везде 5V. Папка 02 на micro SD обязательна.
// Klop 2017 Говорящие часы
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <TM1637Display.h>
#include <SoftwareSerial.h>
#include <mp3TF.h>
#define BusyState 9 // пин BUSY плеера
#define touch 10 // пин сенсора
// TM1637 пины
#define CLK 2
#define DIO 3
TM1637Display display(CLK, DIO);
byte cyfra[]={63,6,91,79,102,109,125,7,127,111};
byte data[] = {0,0,0,0}, prikol, pm;
bool fldv;
tmElements_t tm;
unsigned long interval=0;
mp3TF mp3tf = mp3TF ();
//------------------------------------------------------------
void setup()
{ display.setBrightness(10);
SoftwareSerial mySerial(8, 7); // RX, TX
mySerial.begin (9600);
mp3tf.init (&mySerial);
delay(200);
mp3tf.volumeSet(20);
delay(200);
pinMode(BusyState,INPUT);
pinMode(touch,INPUT);
}
//------------------------------------------------------------
void ozv(int myfile)
{ mp3tf.playFolder2(2,myfile);
delay(300);
while(!digitalRead(BusyState));
}
//------------------------------------------------------------
void golos()
{ ozv(tm.Hour+61);
ozv(tm.Minute+1);
}
//------------------------------------------------------------
void mydelay(long md)
{unsigned long starttime=millis();
while (millis()-starttime<md)
if (digitalRead(touch)) golos();
}
//------------------------------------------------------------
void hh()
{ getdt();
if (fldv) data[1]= data[1]|0x80;
display.setSegments(data);
}
//------------------------------------------------------------
void getdt()
{ if (RTC.read(tm))
{if (tm.Hour<10) data[0]=0;
else data[0]=cyfra[tm.Hour/10];
data[1]=cyfra[tm.Hour%10];
data[2]=cyfra[tm.Minute/10];
data[3]=cyfra[tm.Minute%10];
} else data[0]=8;
}
//------------------------------------------------------------
void loop()
{
hh();
mydelay(500);
fldv=!fldv;
}
Мы увидим на дисплее текущее время и моргающее двоеточие-секунды. А теперь прикоснемся к сенсору и вот оно! Часы сообщают нам время голосом. Такие часы я видел когда-то очень давно и мне очень хотелось тогда их иметь, но, увы, возможностей не было. Теперь, не особо напрягаясь, любой может собрать их за несколько минут. Но мы пойдем еще дальше и дадим часам сварливый характер. Пусть наши часы начнут ворчать, если к сенсору прикасаться слишком часто.
Для этого добавим несколько файлов с соответствующей озвучкой и несколько строк кода. Обновленный скетч будет выглядеть так:
Говорящие часы - ворчуны )
Таблица соединений:
Сенсор сигнал — Pin 10
Mp3 модуль RX — Pin 8 (через резистор 2к)
Mp3 модуль TX — Pin 7 (через резистор 2к)
Mp3 модуль Busy — Pin 9
TM1637 CLK — Pin 2
TM1637 DIO — Pin 3
DS1307 Sda — Pin 4
DS1307 Scl — Pin 5
Питание везде 5V. Папка 02 на micro SD обязательна.
Сенсор сигнал — Pin 10
Mp3 модуль RX — Pin 8 (через резистор 2к)
Mp3 модуль TX — Pin 7 (через резистор 2к)
Mp3 модуль Busy — Pin 9
TM1637 CLK — Pin 2
TM1637 DIO — Pin 3
DS1307 Sda — Pin 4
DS1307 Scl — Pin 5
Питание везде 5V. Папка 02 на micro SD обязательна.
// Klop 2017 Говорящие часы - ворчуны )
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <TM1637Display.h>
#include <SoftwareSerial.h>
#include <mp3TF.h>
#define BusyState 9 // пин BUSY плеера
#define touch 10 // пин сенсора
// TM1637 пины
#define CLK 2
#define DIO 3
TM1637Display display(CLK, DIO);
byte cyfra[]={63,6,91,79,102,109,125,7,127,111};
byte data[] = {0,0,0,0}, prikol, pm;
bool fldv;
tmElements_t tm;
unsigned long interval=0;
mp3TF mp3tf = mp3TF ();
//------------------------------------------------------------
void setup()
{ display.setBrightness(10);
SoftwareSerial mySerial(8, 7); // RX, TX
mySerial.begin (9600);
mp3tf.init (&mySerial);
delay(200);
mp3tf.volumeSet(20);
delay(200);
pinMode(BusyState,INPUT);
pinMode(touch,INPUT);
}
//------------------------------------------------------------
void ozv(int myfile)
{ mp3tf.playFolder2(2,myfile);
delay(300);
while(!digitalRead(BusyState));
}
//------------------------------------------------------------
void golos()
{ unsigned long i; // начало кода ворчалок
i=millis()-interval;
interval=millis();
if (i<5000 && pm==tm.Minute)
{prikol++;
if (prikol==1) ozv(85); else
if (prikol==2) ozv(86); else
if (prikol==3) ozv(87);
delay(200);
} else prikol=0;
if (prikol==0) pm=tm.Minute;
if (prikol<3) // конец кода ворчалок
{ozv(tm.Hour+61);
ozv(tm.Minute+1);
}
}
//------------------------------------------------------------
void mydelay(long md)
{unsigned long starttime=millis();
while (millis()-starttime<md)
if (digitalRead(touch)) golos();
}
//------------------------------------------------------------
void hh()
{ getdt();
if (fldv) data[1]= data[1]|0x80;
display.setSegments(data);
}
//------------------------------------------------------------
void getdt()
{ if (RTC.read(tm))
{if (tm.Hour<10) data[0]=0;
else data[0]=cyfra[tm.Hour/10];
data[1]=cyfra[tm.Hour%10];
data[2]=cyfra[tm.Minute/10];
data[3]=cyfra[tm.Minute%10];
} else data[0]=8;
}
//------------------------------------------------------------
void loop()
{
hh();
mydelay(500);
fldv=!fldv;
}
А вот как они работают.
Теперь, если касаться сенсора чаще, чем раз в 5 сек, часы начинают раздражаться, а после 3-го раза вообще обижаются и перестают сообщать время. Но, конечно, ненадолго ). После 5 секунд настроение у них снова восстанавливается. Характеры можно придумать разные, все ограничивается только нашей фантазией. Собрав подобные часы в корпусе (мыльница?), мы получим отличный подарок для близких. И даже весьма полезный для слабовидящих. А может кому то понравится возможность узнавать ночью время, не открывая глаз, а просто коснувшись корпуса часов рядом. Себе-то я точно соберу, как только приедет микрофон (чтобы в зависимости от шумности вокруг, регулировалась громкость).
Собирая часы, мы использовали полный набор файлов с часами и минутами, где в каждом файле присутствует число и его размерность: “Два часа”, “Двадцать одна минута”, “Восемнадцать часов” и т.д. Нам не пришлось заниматься лексическим разбором: “одна минутА”, но “две минутЫ” или “пять минуТ”. Мы упростили себе жизнь, соединяя только 2 файла – количество часов и количество минут. А комбинаций всех возможных часов и минут получилось всего 84, это немного и нас это устроило. Но мы можем озвучить не только часы, но и показания любых других приборов, значения которых могут превышать тысячи и более. В этом случае все возможные комбинации не надиктуешь. Значит нужно написать универсальный голосовой синтезатор чисел. Этим и займемся.
Как мы произносим число? Есть базовые лексические единицы и сборные. Например, числа “один”, “пятнадцать”, “семьдесят”, “сто” являются базовыми, а число “сто двадцать пять” синтезируется из 3-х базовых единиц. В интервале от 0 до 19 числа все базовые, поэтому надиктуем их в 20 файлов. Далее идут десятки от 20 до 90, их также запишем в 8 файлов. За ними 9 файлов с произношением сотен. Дальше начинаются тысячи. Тут уже новый лексический нюанс. “ОДНА тысячА”, “ДВЕ тысячИ” и “пять тысяЧ”. Почти все в тысячах будет соответствовать и для более высоких размерностей, кроме слов “ОДНА” и “ДВЕ”. Для всех остальных размерностей это будут слова “ОДИН” и “ДВА”. Надиктуем эти варианты произношения для тысяч в 2 файла и добавим их в нашу папку. А затем по три варианта произношения для каждой последующей размерности: «миллион», “миллиона”, “миллионов” и т.д.
Все файлы надиктованы, идея понятна, осталось воплотить её в скетч. Но мы-же программисты
Скетч голосового синтезатора чисел
Таблица соединений:
Mp3 модуль RX — Pin 8 (через резистор 2к)
Mp3 модуль TX — Pin 7 (через резистор 2к)
Mp3 модуль Busy — Pin 9
Питание везде 5V. Папка 01 на micro SD обязательна.
Mp3 модуль RX — Pin 8 (через резистор 2к)
Mp3 модуль TX — Pin 7 (через резистор 2к)
Mp3 модуль Busy — Pin 9
Питание везде 5V. Папка 01 на micro SD обязательна.
//Klop 2017 говорилка чисел
#include <SoftwareSerial.h>
#include <mp3TF.h>
#define BusyState 9 // пин BUSY плеера
#define c19 19
#define c100 29
#define c1000 38
#define odna 76
#define dve 77
bool fl;
mp3TF mp3tf = mp3TF ();
char ccc[3];
byte troyka [3];
//------------------------------------------------------------
void setup()
{ SoftwareSerial mySerial(8, 7); // RX, TX
mySerial.begin (9600);
mp3tf.init (&mySerial);
delay(200);
pinMode(BusyState,INPUT);
mp3tf.volumeSet(22);
delay(200);
}
//------------------------------------------------------------
void ozv(int myfile)
{ mp3tf.playFolder2(1,myfile);
delay(200);
while(!digitalRead(BusyState));
}
//------------------------------------------------------------
void voicedig(char cc[])
{ int a,b,c,d,jj,sme,dp;
a=strlen(cc);
if (atoi(cc)==0) ozv(1); else
{
for (byte i=0;i<3;i++) ccc[i]=0;
b=a%3;c=a/3;jj=0;
for (byte i=0;i<c+1;i++)
{strncpy(ccc,cc+jj,b);
d=atoi(ccc); a=d;
for (byte i=0;i<3;i++)
{ troyka[2-i]=a%10;
a=a/10;
}
if (d>0)
{ dp=troyka[2];
if (c-i==1)
if (troyka[2]==1) dp=odna;
else if(troyka[2]==2) dp=dve;
if (troyka[0]>0) ozv(c100+troyka[0]-1);
if (troyka[1]>1) ozv(c19+troyka[1]);
else if (troyka[1]==1)
{ozv(troyka[1]*10+troyka[2]+1);
goto m1;
}
if (troyka[2]>0) ozv(dp+1);
m1: a=d%100;
if (a>19) a=d%10;
if (a==1) sme=0; else
if (a>1 && a<5) sme=1; else sme=2;
if (c-i>0) ozv(c1000+(c-i-1)*3+sme);
};
jj=jj+b;b=3;
delay(100);
}
}
}
//------------------------------------------------------------
void loop()
{
voicedig("91352412028529003471097014534460011762920");
while(true);
}
Вот такая небольшая функция voicedig() способна лексически верно произнести любое натуральное число до 42-х разрядов. А вы сможете? Тогда вперед! )))
Видео работы голосового синтезатора:
Если кому-то этого недостаточно, можно надиктовать еще размерностей, но мне их названия неизвестны. (Не проблема обучить систему произношению дробей и еще многому чему, но для демонстрации возможностей сабжа вполне, думаю, приведенного примера будет достаточно.)
Теперь давайте применим нашу функцию для реальных измерений. Для этого добавим на макетку датчик освещенности BH1750. Значит, нам понадобятся еще 3 файла со словами “люкс”, “люкса”, “люксов”. Закинем эти файлы в новую папку и напишем скетч для опроса датчика. Немножко похулиганим и добавим в инициализацию прибора прибора приветствие ).
Скетч говорящего датчика освещенности
Таблица соединений:
Сенсор сигнал — Pin 10
Mp3 модуль RX — Pin 8 (через резистор 2к)
Mp3 модуль TX — Pin 7 (через резистор 2к)
Mp3 модуль Busy — Pin 9
TM1637 CLK — Pin 2
TM1637 DIO — Pin 3
BH1750 Sda — Pin 4
BH1750 Scl — Pin 5
Питание везде 5V. Папки 01, 03 и 04 на micro SD обязательны.
Сенсор сигнал — Pin 10
Mp3 модуль RX — Pin 8 (через резистор 2к)
Mp3 модуль TX — Pin 7 (через резистор 2к)
Mp3 модуль Busy — Pin 9
TM1637 CLK — Pin 2
TM1637 DIO — Pin 3
BH1750 Sda — Pin 4
BH1750 Scl — Pin 5
Питание везде 5V. Папки 01, 03 и 04 на micro SD обязательны.
// Klop 2017 говорящий люксметр
#include <Wire.h>
#include <BH1750.h>
#include <TM1637Display.h>
#include <SoftwareSerial.h>
#include <mp3TF.h>
#define BusyState 9 // пин BUSY плеера
#define touch 10 // пин сенсора
#define c19 19
#define c100 29
#define c1000 38
#define odna 76
#define dve 77
#define f1 1 // папка с числами
#define f4 4 // папка с приветствием
// TM1637 пины
#define CLK 2
#define DIO 3
#define f3 3 // папка с файлами люксов
TM1637Display display(CLK, DIO);
byte cyfra[]={63,6,91,79,102,109,125,7,127,111};
byte data[] = {0,0,0,0}, prikol, pm;
mp3TF mp3tf = mp3TF ();
char str[100];
int lp=0;
uint16_t lux;
BH1750 lightMeter;
char ccc[3]; byte troyka [3];
//------------------------------------------------------------
void setup()
{ Serial.begin(9600);
lightMeter.begin();
display.setBrightness(10);
SoftwareSerial mySerial(8, 7); // RX, TX
mySerial.begin (9600);
mp3tf.init (&mySerial);
delay(200);
mp3tf.volumeSet(20);
delay(200);
pinMode(BusyState,INPUT);
pinMode(touch,INPUT);
ozv(f4,1);
}
//------------------------------------------------------------
void ozv(int papka, int myfile)
{ mp3tf.playFolder2(papka,myfile);
delay(300);
while(!digitalRead(BusyState));
}
//------------------------------------------------------------
void voicedig(char cc[])
{ int a,b,c,d,jj,sme,dp;
a=strlen(cc);
for (byte i=0;i<3;i++) ccc[i]=0;
b=a%3;c=a/3;jj=0;
for (byte i=0;i<c+1;i++)
{strncpy(ccc,cc+jj,b);
d=atoi(ccc); a=d;
for (byte i=0;i<3;i++)
{ troyka[2-i]=a%10;
a=a/10;
}
if (d>0)
{ dp=troyka[2];
if (c-i==1)
if (troyka[2]==1) dp=odna;
else if(troyka[2]==2) dp=dve;
if (troyka[0]>0) ozv(f1,c100+troyka[0]-1);
if (troyka[1]>1) ozv(f1,c19+troyka[1]);
else if (troyka[1]==1)
{ozv(f1,troyka[1]*10+troyka[2]+1);
goto m1;
}
if (troyka[2]>0) ozv(f1,dp+1);
m1: a=d%100;
if (a>19) a=d%10;
if (a==1) sme=0; else
if (a>0 && a<5) sme=1; else sme=2;
if (c-i>0) ozv(f1,c1000+(c-i-1)*3+sme);
}
jj=jj+b;b=3;
delay(100);
}
}
//------------------------------------------------------------
void mydelay(long md)
{unsigned long starttime=millis();
while (millis()-starttime<md)
if (digitalRead(touch) && lux>0)
{sprintf(str, "%u", lux );
voicedig(str);
byte a=lux%100;
if (a>19) a=lux%10;
if (a==1) ozv(f3,1); else
if (a>0 && a<5) ozv(f3,2); else
ozv(f3,3);
}
}
//------------------------------------------------------------
void hh()
{ getdt();
display.setSegments(data);
}
//------------------------------------------------------------
void getdt()
{ int a=lux;
for (byte i=0;i<4;i++)
{ data[3-i]=cyfra[a%10];
a=a/10;
}
}
//------------------------------------------------------------
void loop()
{ lux = lightMeter.readLightLevel();
hh();
mydelay(2000);
}
Видео работы говорящего люксметра:
Заменим люксметр на дальномер, поправим немного код и получим говорящий дальномер.
Скетч говорящего дальномера
Таблица соединений:
Сенсор сигнал — Pin 10
Mp3 модуль RX — Pin 8 (через резистор 2к)
Mp3 модуль TX — Pin 7 (через резистор 2к)
Mp3 модуль Busy — Pin 9
TM1637 CLK — Pin 2
TM1637 DIO — Pin 3
HC-SR04 trigPin — Pin 11
HC-SR04 echoPin — Pin 12
Питание везде 5V. Папка 05 на micro SD обязательна.
Сенсор сигнал — Pin 10
Mp3 модуль RX — Pin 8 (через резистор 2к)
Mp3 модуль TX — Pin 7 (через резистор 2к)
Mp3 модуль Busy — Pin 9
TM1637 CLK — Pin 2
TM1637 DIO — Pin 3
HC-SR04 trigPin — Pin 11
HC-SR04 echoPin — Pin 12
Питание везде 5V. Папка 05 на micro SD обязательна.
// Klop 2017
#include <TM1637Display.h>
#include <SoftwareSerial.h>
#include <mp3TF.h>
#define BusyState 9 // пин BUSY плеера
#define touch 10 // пин сенсора
#define c19 19
#define c100 29
#define c1000 38
#define odna 76
#define dve 77
#define f1 1 // папка с числами
#define f5 5 // папка с милиметрами
#define trigPin 11 // пины
#define echoPin 12 // дальномера
// TM1637 пины
#define CLK 2
#define DIO 3
TM1637Display display(CLK, DIO);
byte cyfra[]={63,6,91,79,102,109,125,7,127,111};
byte data[] = {0,0,0,0}, prikol, pm;
mp3TF mp3tf = mp3TF ();
char str[100];
int lp=0;
char ccc[3]; byte troyka [3];
int mm;
//------------------------------------------------------------
void setup()
{ Serial.begin(9600);
display.setBrightness(10);
SoftwareSerial mySerial(8, 7); // RX, TX
mySerial.begin (9600);
mp3tf.init (&mySerial);
delay(200);
mp3tf.volumeSet(20);
delay(200);
pinMode(BusyState,INPUT);
pinMode(touch,INPUT);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
//------------------------------------------------------------
void ozv(int papka, int myfile)
{ mp3tf.playFolder2(papka,myfile);
delay(300);
while(!digitalRead(BusyState));
}
//------------------------------------------------------------
void voicedig(char cc[])
{ int a,b,c,d,jj,sme,dp;
a=strlen(cc);
for (byte i=0;i<3;i++) ccc[i]=0;
b=a%3;c=a/3;jj=0;
for (byte i=0;i<c+1;i++)
{strncpy(ccc,cc+jj,b);
d=atoi(ccc); a=d;
for (byte i=0;i<3;i++)
{ troyka[2-i]=a%10;
a=a/10;
}
if (d>0)
{ dp=troyka[2];
if (c-i==1)
if (troyka[2]==1) dp=odna;
else if(troyka[2]==2) dp=dve;
if (troyka[0]>0) ozv(f1,c100+troyka[0]-1);
if (troyka[1]>1) ozv(f1,c19+troyka[1]);
else if (troyka[1]==1)
{ozv(f1,troyka[1]*10+troyka[2]+1);
goto m1;
}
if (troyka[2]>0) ozv(f1,dp+1);
m1: a=d%100;
if (a>19) a=d%10;
if (a==1) sme=0; else
if (a>0 && a<5) sme=1; else sme=2;
if (c-i>0) ozv(f1,c1000+(c-i-1)*3+sme);
}
jj=jj+b;b=3;
delay(100);
}
}
//------------------------------------------------------------
void mydelay(long md)
{unsigned long starttime=millis();
while (millis()-starttime<md)
if (digitalRead(touch) && mm>0)
{sprintf(str, "%u", mm );
voicedig(str);
byte a=mm%100;
if (a>19) a=mm%10;
if (a==1) ozv(f5,1); else
if (a>0 && a<5) ozv(f5,2); else
ozv(f5,3);
}
}
//------------------------------------------------------------
void hh()
{ getdt();
display.setSegments(data);
}
//------------------------------------------------------------
void getdt()
{ int a=mm;
for (byte i=0;i<4;i++)
{ data[3-i]=cyfra[a%10];
a=a/10;
}
}
//------------------------------------------------------------
void loop()
{ int duration;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
mm = duration / 5.8;
hh();
mydelay(2000);
}
Видео работы говорящего дальномера:
Конечно, данные датчики используют нашу функцию лишь на малую долю. Но ее возможностей хватит на любой прибор с запасом.
Напоследок мы модернизируем игру Тетрис из этого обзора. Отключим уже ненужную пьезопищалку и поставим сабж. Запишем фоновый трек, звук проваливания линии и озвучим установление нового рекорда. Применим из вышеозвученной библиотеки крайне интересные функции: repeatCurrent(bool start) (Проигрывать по кругу текущий трек) и setInterCut(uint16_t track)(ставит текущий проигрываемый трек на паузу и проигрывает заданный).
Тетрис с озвучкой
Таблица соединений:
Serial clock out (SCLK) — pin 3
Serial data out (DIN) — pin 4
Data/Command select (D/C) — pin 5
LCD chip select (CS) — pin 7
LCD reset (RST) — pin 7
Mp3 модуль RX — Pin 10 (через резистор 2к)
Mp3 модуль TX — Pin 11 (через резистор 2к)
Mp3 модуль Busy — Pin 12
Питание везде 5V. Папки 06 и ADVERT на micro SD обязательны.
Serial clock out (SCLK) — pin 3
Serial data out (DIN) — pin 4
Data/Command select (D/C) — pin 5
LCD chip select (CS) — pin 7
LCD reset (RST) — pin 7
Mp3 модуль RX — Pin 10 (через резистор 2к)
Mp3 модуль TX — Pin 11 (через резистор 2к)
Mp3 модуль Busy — Pin 12
Питание везде 5V. Папки 06 и ADVERT на micro SD обязательны.
//// © Klop 2017
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <IRremote.h>
#include <EEPROM2.h>
#include <SoftwareSerial.h>
#include <mp3TF.h>
#define BusyState 12 // пин BUSY плеера
#define rk 4 // ширина квадратика
#define rz 5 // ширина места
#define smeX 1
#define smeY 1
#define MaxX 10 // стакан кол-во мест по гориз
#define speaker 9
#define RECV_PIN 8 // нога на IRDA приемник
// pin 3 - Serial clock out (SCLK)
// pin 4 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 6 - LCD chip select (CS)
// pin 7 - LCD reset (RST)
Adafruit_PCD8544 display = Adafruit_PCD8544(3, 4, 5, 6, 7);
IRrecv irrecv(RECV_PIN);
decode_results results;
byte mstacan[MaxX][30];
byte Lst,SmeH, center, NumNext;
byte MaxY; // стакан кол-во мест по вертик
int dxx, dyy, FigX, FigY, Victory, myspeed,tempspeed;
unsigned long ok, levo, pravo, vniz, myrecord;
unsigned long flfirst=12345; // метка первого запуска
mp3TF mp3tf = mp3TF ();
byte fig[][4][4]=
{
{{0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0}},
{{0,1,0,0},
{0,1,0,0},
{0,1,0,0},
{0,1,0,0}},
{{0,0,0,0},
{0,1,1,0},
{0,1,1,0},
{0,0,0,0}},
{{0,1,0,0},
{0,1,1,0},
{0,0,1,0},
{0,0,0,0}},
{{0,1,0,0},
{0,1,0,0},
{0,1,1,0},
{0,0,0,0}},
{{0,1,0,0},
{0,1,1,0},
{0,1,0,0},
{0,0,0,0}},
{{0,0,1,0},
{0,1,1,0},
{0,1,0,0},
{0,0,0,0}},
{{0,0,1,0},
{0,0,1,0},
{0,1,1,0},
{0,0,0,0}},
{{0,0,0,0}, //8
{0,0,0,0},
{0,0,0,0},
{0,0,0,0}}
};
//==============================================
void mybeep() // звук
{ mp3tf.setInterCut(2);
delay(800);
}
//==============================================
void figmove(byte a, byte b)
{
for (byte i=0;i<4;i++)
for (byte j=0;j<4;j++)
fig[a][i][j]=fig[b][i][j];
}
//==============================================
void figinit(byte a)
{
for (byte i=0;i<4;i++)
for (byte j=0;j<4;j++)
{
fig[0][i][j]=fig[NumNext][i][j];
if (fig[a][j][i]==1) // покажем след фигуру
display.fillRect(i*rz+60, 20+(j)*rz, rk , rk, BLACK);
else display.fillRect(i*rz+60, 20+(j)*rz, rk , rk, WHITE);
}
display.display();
NumNext=a;
tempspeed=myspeed-(Victory/30)*50; // через каждые 30 линий увеличим скорость падения;
dxx=0;
for (byte i=0;i<MaxX;i++)
if (mstacan[i][3]==2) {mp3tf.stop();
delay(200);
newgame();}
}
//==============================================
void viewstacan()
{
display.drawLine(0,0,0,display.height()-1, BLACK);
display.drawLine(0,display.height()-1,Lst, display.height()-1, BLACK);
display.drawLine(Lst,display.height()-1,Lst,0, BLACK);
for (byte j=4;j<MaxY;j++)
for (byte i=0;i<MaxX;i++)
if (mstacan[i][j]>0)
display.fillRect(i*rz+1, SmeH+(j-4)*rz, rk , rk, BLACK);
else display.fillRect(i*rz+1, SmeH+(j-4)*rz, rk , rk, WHITE);
ds(Victory,1);
display.display();
}
//================================================
void ds(int aa, int b)
{
display.fillRect(55, 10, 29, 10, WHITE);
display.setCursor(55,b*10);
display.println(aa);
}
//================================================
bool iffig(int dx, int dy)
{int i,j;
bool flag=true; bool pov=false;
for (i=0;i<MaxX;i++)
for (j=0;j<MaxY;j++)
if (mstacan[i][j]==1)
mstacan[i][j]=0; // убрали временно фигуру
if (dx==2) // поворот
{ dx=0; pov=true;
figmove(8,0);
for (i=0;i<4;i++)
for (j=0;j<4;j++)
{ fig[0][i][j]=fig[8][3-j][i];
if (fig[0][j][i]==1)
{ if (i+FigX+dx<0) dx=1; // пробуем отодвинуть от стенки слева на 1
if (i+FigX+dx>MaxX-1) dx=-1;// пробуем отодвинуть от стенки справа на 1
}
}
}
for (i=0;i<4;i++)
for (j=0;j<4;j++)
if (fig[0][j][i]==1)
if (i+FigX+dx<0 || i+FigX+dx>MaxX-1 || FigY+j+dy>MaxY-1 || mstacan[i+FigX+dx][FigY+j+dy]>0)
{flag=false; break;} // проверили на новые координаты
if (flag)
{FigX=FigX+dx; FigY=FigY+dy;byte k=0;
for (i=0;i<4;i++)
for (j=0;j<4;j++)
if (fig[0][j][i]==1)
{mstacan[i+FigX][j+FigY]=1;
dxx=0;
}
} // переместили фигуру на новые координаты
else
{ if (pov) figmove(0,8);
for (i=0;i<4;i++) // восстановили фигуру
for (j=0;j<4;j++)
if (fig[0][j][i]==1)
mstacan[i+FigX][j+FigY]=1;
}
return(flag);
}
//================================================
void clearstacan()
{
for (byte i=0;i<MaxX;i++)
for (byte j=0;j<MaxY;j++)
mstacan[i][j]=0;
}
//================================================
unsigned long getbutton(char s[])
{unsigned long tt;
display.fillRect(5, 30, 40, 10, WHITE);
display.setCursor(5,30);
display.print(s);
display.display();
while (true)
if (irrecv.decode(&results)) // ловим код кнопки пульта
{ tt=results.value;
delay(400);
irrecv.resume();
break;
}
return(tt);
}
//================================================
void newgame()
{ unsigned long tb;
dxx=0; dyy=1;
display.setCursor(0,0);
clearstacan();
NumNext=random(7)+1;
figinit(random(7)+1);
center=MaxX/2-2;
FigX=center;FigY=0;
display.setCursor(52,0);
display.println("Линий");
viewstacan();
display.setCursor(5,0);
display.print("Рекорд");
display.setCursor(5,10);
display.print(myrecord);
display.display();
if (Victory>myrecord)
{ myrecord=Victory;
EEPROM_write(16, myrecord);
mp3tf.playFolder2(6,4);
delay(200);
while(!digitalRead(BusyState));
}
display.setCursor(5,20);
delay(2000);irrecv.resume();
display.println("Нажмите");
tb=getbutton(" OK");
if (tb!=ok)
{ ok=tb;
levo=getbutton("Влево");
pravo=getbutton("Вправо");
vniz=getbutton("Вниз");
EEPROM_write(0, ok);
EEPROM_write(4, levo);
EEPROM_write(8, pravo);
EEPROM_write(12, vniz);
}
display.fillRect(5, 0, (MaxX-1)*rz, 40, WHITE);
myspeed=800; tempspeed=myspeed;
Victory=0;
mp3tf.playFolder2(6,3);
}
//================================================
void setup()
{ unsigned long tr; word gg=0;
SoftwareSerial mySerial(10, 11); // RX, TX
mySerial.begin (9600);
mp3tf.init (&mySerial);
delay(200);
pinMode(BusyState,INPUT);
mp3tf.volumeSet(22);
delay(200);
mp3tf.setEQ(MP3_EQ_BASS );
randomSeed(analogRead(0));
irrecv.enableIRIn(); // Старт ресивера IRDA
display.begin();
display.setContrast(55);
display.setTextSize(1);
display.setTextColor(BLACK); // установка цвета текста
display.clearDisplay();
Lst=rz*MaxX; // ширина стакана в пикселях
MaxY=display.height()/rz+4; // Высота стакана в кубиках
SmeH=display.height()%rz; // смещение сверху в пикселях для отображения
random(7);
EEPROM_read(0, ok);
EEPROM_read(4, levo);
EEPROM_read(8, pravo);
EEPROM_read(12, vniz);
EEPROM_read(20, tr);
if (tr==flfirst) EEPROM_read(16, myrecord);
else { myrecord=0;
EEPROM_write(16, myrecord);
EEPROM_write(20, flfirst);
}
newgame();
}
//================================================
void dvoiki()
{
for (byte i=0;i<MaxX;i++)
for (byte j=0;j<MaxY;j++)
if (mstacan[i][j]==1)
mstacan[i][j]=2;
}
//================================================
void mydelay(int md)
{
unsigned long starttime=millis();
while (millis()-starttime<md)
{
if (irrecv.decode(&results)) // ловим код кнопки пульта
{
if (results.value==levo) dxx=-1; else
if (results.value==pravo) dxx=1; else
if (results.value==ok) dxx=2; else
if (results.value==vniz) {tempspeed=100; md=0;}
if (dxx!=0) {iffig(dxx,0); viewstacan(); }
delay(40); // убираем дребезг
irrecv.resume();
mp3tf.repeatCurrent(false);
}
}
}
//================================================
bool iffs() // есть ли полные строки?
{bool res=false;
for (byte j=0;j<MaxY;j++)
{ bool fl=true;
for (byte i=0;i<MaxX;i++)
if (mstacan[i][MaxY-1-j]==0) fl=false;
if (fl)
{Victory++;
if (myspeed<0) myspeed=0;
res=true; mybeep();
for (byte k=0;k<MaxX;k++)
for (byte n=j;n<MaxY-2;n++)
mstacan[k][MaxY-n-1]=mstacan[k][MaxY-n-2];
viewstacan();
}
}
return(res);
}
//================================================
void loop()
{ if (!iffig(dxx,dyy))
if (!iffig(0,dyy))
{ dvoiki();
while (iffs()) ;
figinit(random(7)+1);
FigX=center;FigY=0;
}
viewstacan();
mydelay(tempspeed);
}
Посмотреть, что вышло можно ниже. Прошу прощение за качество записи, особенно звука. Снимал чем было, но главное, я думаю, оценить можно. Кстати, из видео можно услышать звук проваливания линий. Кто первый догадается откуда он, сразу плюс в карму! ))
Все скетчи, описанные выше, работают с тем набором файлом, под который заточены. Скачать этот набор можно здесь. Разумеется, я не диктор, потому рекомендую тем, кто захочет повторить эти эксперименты, попросить человека с хорошей дикцией надиктовать эти файлы. Главное — соблюдать последовательность.
Итак, мы увидели, что звук может не только заменять дисплеи, но и, дополняя их, заметно разнообразить выдачу информации. Здесь все будет зависеть только от нашей фантазии… и умения немного программировать.
Выводы:
Из минусов я, пожалуй, отмечу только ограничения на имена файлов и папок, поскольку нужно знать не столько имя файла, сколько его место в таблице FAT. Из-за этого, бывало, надиктуешь полсотни файлов с именами 0001-0050, а один в середине пропустишь. И потом приходится вручную переименовывать кучу файлов, чтобы вставить забытый. Поэтому я написал себе программку на Delphi для вставки и удаления файлов в папке, так, что теперь это для меня не проблема, но производителю все-таки нужно подумать над этим.
Плеер обладает огромным потенциалом и, при своей низкой цене и творческом подходе способен стать отличным помощником программиста микроконтроллеров.
Вердикт: Must have!
+299 |
67625
149
|
Самые обсуждаемые обзоры
+73 |
3474
145
|
+51 |
3663
67
|
+31 |
2671
51
|
+39 |
3060
42
|
А провал линии — «Money» «Pink Floyd»
У вас же компьютер!
Программ-переименовщиков предостаточно. Например, лёгонький ReNamer в полтора мегабайта. Да любой другой — стоит загуглить. А уж тупо пронумеровать по порядку — это даже батник наваять может начинающий програмер.
Мне периодически пригождаются.
UART Port
Standard Serial; TTL Level; Baud rate adjustable(default baud rate is 9600)
Однако нужной команды найти не удалось ни в мануале, ни в Гугле…
Кто-нить смог изменить битрейт UARTa в этом модуле?
С меня говорящий Вам +
Плюсанул и пошел заказывать.
P.s по вашей ссылке товар уже не доступен.
просто оставлю.
Как USB DAC. У меня заработал как картридер.
Можно не диктовать числа, а использовать программный синтезатор на ПК, скормив ему текстовый файл. Очень легко и быстро это было проделано на Mac. Даже голос можно выбрать. Уверен что таких программ полно.
total commander умеет переименовывать + ещё 1000 фукнкий
https://aliexpress.com/item/item/voice-playback-module-MP3-voice-prompts-voice-broadcast-device/32493850856.html
и просто ставил параллельно для каждой кнопки специальное реле 24В (9 штук) и оно замыкало контакты звуковой платы. Конечно, такой способ довольно затратный.
Хотелось бы пойти дальше.
Привинтить как то с распознанием голоса и команд, типа:
Приходишь с улицы — зажигается свет и на фоне легкой музыки мягкий голос:
— Приветствую (имя), какие будут пожелания?
-Кузя люстра ярко
-Кузя погода?
-Кузя музыка тихо
-Кузя закажи пицу :))))
Не уверен, что получится по-русски, но на английском примеров полно в сети.
Должно получится как-то так: «Kuzya, one bottle of vodka and talk to me now».
Типа, небольшая коробочка\брелок (со спичечную) с батарейкой, динамиком и кнопкой.
Жмёшь кнопку — играет закинутый файл, пусть в районе 30 секунд.
Типа продвинутый блок от музыкальной открытки или говорящей куклы.
2 мегабайта памяти на борту, для озвучки большинства проектов должно хватить
Подправил, добавил строку else ozv(f1,1);:
void voicedig(char cc[])
{ int a,b,c,d,jj,sme,dp;
a=strlen(cc);
for (byte i=0;i<3;i++) ccc[i]=0;
b=a%3;c=a/3;jj=0;
for (byte i=0;i<c+1;i++)
{strncpy(ccc,cc+jj,b);
d=atoi(ccc); a=d;
for (byte i=0;i<3;i++)
{ troyka[2-i]=a%10;
a=a/10;
}
if (d>0)
{ dp=troyka[2];
if (c-i==1)
if (troyka[2]==1) dp=odna;
else if(troyka[2]==2) dp=dve;
if (troyka[0]>0) ozv(f1,c100+troyka[0]-1);
if (troyka[1]>1) ozv(f1,c19+troyka[1]);
else if (troyka[1]==1)
{ozv(f1,troyka[1]*10+troyka[2]+1);
goto m1;
}
if (troyka[2]>=0) ozv(f1,dp+1);
m1: a=d%100;
if (a>19) a=d%10;
if (a==1) sme=0; else
if (a>1 && a<5) sme=1; else sme=2;
if (c-i>0) ozv(f1, c1000+(c-i-1)*3+sme);
}
else ozv(f1,1);
jj=jj+b;b=3;
delay(100);
}
Но проблема скетч синтезатора чисел выдаёт ошибку на этой строке: else ozv(f1,1); Говорит, что 'f1' was not declared in this scope
Как вылечить?