Описание
В этом уроке мы сделаем музыкальную шкатулку на основе Raspberry Pi и Flash-I2C модулей Бампер с 9-ю датчиками линий, Мотор-редуктор с управляющим контроллером и Энкодер. Мотор будет крутить барабан с нарисованными от руки нотами, датчики линий будут регистрировать наличие нот, а Raspberry PI будет проигрывать соответствующие звуки.
Видео
Нам понадобится
Аппаратная часть
- Raspberry Pi
- Набор Сделай сам - "Музыкальная шкатулка"
- Trema+Expander Hat
- i2C HUB
- Бампер с 9-ю датчиками линий, FLASH-I2C
- Мотор-редуктор с управляющим контроллером, FLASH-I2C
- Энкодер, потенциометр, FLASH-I2C
Программная часть
- Библиотека pyfluidsynth
- Файл - звуковой шрифт (soundfont)
- Библиотека pyiArduinoI2Cbumper
- Библиотека pyiArduinoI2Cmotor
- Библиотека pyiArduinoI2Cencoder
Подключение
Перед подключением необходимо установить адреса модулей.
В нашем случае это адреса 0x0a для модуля Бампер, 0x0b для модуля Мотор и 0x0c для модуля Энкодер в шестнадцатеричной системе счисления.
- Подробнее про установку адресов модулей с технологией Flash-i2C
- Смена адреса Бампера на шине I2C
- Смена адреса Мотора на шине I2C
- Смена адреса Энкодера на шине I2C

Установка библиотек
Откроем эмулятор терминала
Для проекта понадобится установить библиотеки pyfluidsynth,pyiArduinoI2Cbumper и pyiArduinoI2Cmotorв уже существующую или новую виртуальную среду
Введём строку ниже в окно терминала и нажмём <enter>
pip install pyiArduinoI2Cbumper pyiArduinoI2Cmotor pyiArduinoI2Cencoder pyfluidsynth
Подробнее об установке библиотек можно узнать в этой статье.
Cкрипт проекта
Скрипт можно запустить из эмулятора терминала, сохранив его в файл например под названием musicbox.py, командой python musicbox.py или скопировать содержимое скрипта в программу Thonny Python IDE и запустить его там. В Thonny IDE так же необходимо настроить виртуальную среду и установить в ней вышеперечисленные библиотеки. Подробнее об установке библиотек можно узнать в этой
статье.
font = synth.sfload('/home/pi/Downloads/soundfont.sf2'
Для написания скриптов мы будем использовать рекомендованный Raspberry Pi Foundation редактор Thonny Python IDE. Для запуска нажмём на кнопку меню в правом верхнем углу, затем Программирование->Thonny Python IDE:

Откроется редактор, можем приступать к написанию скриптов:

Для работы с модулей необходимо включить шину I2C.
Ниже приведён универсальный скетч, который позволяем менять инструменты нажатием на кнопку энкодера, а также изменять скорость вращения барабана. Помимо этого, в конце статьи вы найдёте уже готовые ленты для барабанов с записанными мелодиями и скетчи к ним, в которых настроены необходимые инструменты.
# Подключаем библиотеки
import fluidsynth
from pyiArduinoI2Cbumper import *
from pyiArduinoI2Cmotor import *
from pyiArduinoI2Cencoder import *
from time import sleep
# Адреса модулей i2c
BUM_ADDRESS = 0x0a
MOT_ADDRESS = 0x0b
ENC_ADDRESS = 0x0c
# Диапазон энкодера
ENC_RANGE = 2
#Начальная скорость мотора (ШИМ)
MOT_SPEED = 6
# Константы с номерами инструментов
SOLO = [76, 55, 31, 25, 1, 8, 42, 56, 82]
BASS = 37
BANK = 0
# Константы дорожек инструментов
BASS_TRACK = 0
SOLO_TRACK = 1
# Константы динамики инструментов
BASS_VEL = 127
SOLO_VEL = 90
# Констаната разделения инструментов по датчикам
SPLIT = 5
# Создаём объект бампера
bum = pyiArduinoI2Cbumper(address = BUM_ADDRESS, bus = "/dev/i2c-3")
# Создаём объект мотора
mot = pyiArduinoI2Cmotor(address = MOT_ADDRESS, bus = "/dev/i2c-3")
# Создаём объект энкодера
enc = pyiArduinoI2Cencoder(address = ENC_ADDRESS, bus = "/dev/i2c-3")
# Устанавливаем диапазон энкодера
enc.setPosSettings(ENC_RANGE)
# Создаём объект синтезатора
synth = fluidsynth.Synth()
# Включаем синтезатор, указывая драйвер
synth.start(driver='alsa')
# Подключаем файл звукового шрифта.
# Если файл шрифта не лежит в одной папке
# с этим скриптом, укажите абсолютный путь здесь
font = synth.sfload('soundfont.sf2')
# Выбираем программу синтезатора для каждого инструмента
# Полный список названий General Midi звуков -
# https://soundprogramming.net/file-formats/general-midi-instrument-list/
synth.program_select(BASS_TRACK, font, BANK, BASS)
synth.program_select(SOLO_TRACK, font, BANK, SOLO[0])
# Гамма для проигрывания (в данном случае пентатоника Ля-минор)
# Цифры - ноты по номенклатуре MIDI, где 60 - нота ДО малой октавы
scale = [ 81, 78, 76, 74, 71, 33, 31, 28, 26 ]
# Список проигрываемых нот
is_playing = [False] * len(scale)
# Индекс для списка с тембрами
index = 0
try:
# Запускаем мотор
mot.setSpeed(MOT_SPEED, MOT_PWM)
# Считываем положение энкодера
p = enc.getPosition()
while True:
# Считываем положение энкодера в бесконечном цикле
new_p = enc.getPosition()
# Если положение энкодера изменилось:
if p != new_p:
p = new_p
# меняем скорость мотора
mot.setSpeed(p, MOT_PWM)
# Если кнопка была нажата и отпущена
if enc.getButton(KEY_RELEASED):
# Приращиваем индекс для списка с тембрами
index += 1
# Проверяем диапазон списка с тембрами
if index >= len(SOLO):
index = 0
# Меняем инструмент на следующий списка с тембрами
synth.program_select(SOLO_TRACK, font, BANK, SOLO[index])
# Читаем состояние всех датчиков линий
readall = bum.getLineDigital(BUM_LINE_ALL)
for i in range(len(scale)):
# Если бит датчика установлен
if readall & (1 << i + 1):
track = 0
track_vel = 100
# Выбор инструмента
if i < SPLIT:
# Соло
track = SOLO_TRACK
track_vel = SOLO_VEL
else:
# Бас
track = BASS_TRACK
track_vel = BASS_VEL
# Если нота ещё не играет
if is_playing[i] == False:
# играем ноту
synth.noteon(track, scale[i], track_vel)
# Устанавливаем, что нота уже играет
is_playing[i] = True
else:
track = 0
# Выбор инструмента
if i < SPLIT:
# Соло
track = SOLO_TRACK
else:
# Бас
track = BASS_TRACK
# Выбор ноты
if is_playing[i] == True:
# Выключаем ноту
synth.noteoff(track, scale[i])
# Устанавливаем, что нота больше не играет
is_playing[i] = False
# Если выполнения скрипта прервано - посылаем сигнал закончить все ноты
# и остановить мотор
except:
for i in scale:
synth.noteoff(BASS_TRACK, i)
synth.noteoff(SOLO_TRACK, i)
mot.setSpeed(0, MOT_PWM)
Настройка автозапуска
Если есть необходимость запускать скрипт при загрузке Raspberry OS - то необходимо добавить путь к скрипту в файл /etc/rc.local (текстовый редактор должен быть запущен от имени суперпользователя) и установить все библиотеки от имени суперпользователя. Для этого необходимо установить их с командой sudo. Например sudo pip3 install pyiArduinoI2Cbumper pyiArduinoI2Cmotor.
Пример файла rc.local для автозапуска скрипта проекта. Путь к скрипту проекта должен быть указано до exit 0.
#!/bin/sh -e # # rc.local # # Этот скрипт запускается в конце загрузки любого многопользовательского режима. # Скрипт должен заканчиваться строчкой "exit 0" при успешном завершении # или с кодом ошибки в ином случае # # Для включения или выключения скрипта достаточно установить биты # разрешений. Например: # Не запускать скрипт "sudo chmod -x /etc/rc.local" # Запускать скрипт "sudo chmod +x /etc/rc.local" # # Вывести IP адрес _IP=$(hostname -I) || true if [ "$_IP" ]; then printf "My IP address is %s\n" "$_IP" fi # Запустить скрипт музыкальной шкатулки python3 /home/pi/Python/musicbox.py exit 0

Обсуждение