КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Музыкальная шкатулка на Raspberry Pi при помощи Бампера с 9 датчиками линий

Описание

В этом уроке мы сделаем музыкальную шкатулку на основе Raspberry Pi и Flash-I2C модулей Бампер с 9-ю датчиками линий, Мотор-редуктор с управляющим контроллером и Энкодер. Мотор будет крутить барабан с нарисованными от руки нотами, датчики линий будут регистрировать наличие нот, а Raspberry PI будет проигрывать соответствующие звуки.

Видео

Нам понадобится

Аппаратная часть

Программная часть

Подключение

Перед подключением необходимо установить адреса модулей.

В нашем случае это адреса 0x0a для модуля Бампер, 0x0b для модуля Мотор и 0x0c для модуля Энкодер в шестнадцатеричной системе счисления.

Установка библиотек

Откроем эмулятор терминала



Для проекта понадобится установить библиотеки pyfluidsynth,pyiArduinoI2Cbumper и pyiArduinoI2Cmotor.

Введём строку ниже в окно терминала и нажмём <enter>

pip3 install pyiArduinoI2Cbumper pyiArduinoI2Cmotor pyiArduinoI2Cencoder pyfluidsynth

Подробнее об установке библиотек можно узнать в этой статье.

Cкрипт проекта

Скрипт можно запустить из эмулятора терминала, сохранив его в файл например под названием musicbox.py, командой python3 musicbox.py или скопировать содержимое скрипта в программу Thonny Python IDE и запустить его там.

В папке со скриптом должен лежать скачанный файл sountfont.sf2 или в сценарии должен быть указан абсолютный путь к файлу скрипта, например: 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(BUM_ADDRESS)

# Создаём объект мотора
mot = pyiArduinoI2Cmotor(MOT_ADDRESS)

# Создаём объект энкодера
enc = pyiArduinoI2Cencoder(ENC_ADDRESS)

# Устанавливаем диапазон энкодера
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

Ссылки




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями