MIDI - USB клавиатура на Arduino Nano

Тема в разделе "Глядите, что я сделал", создана пользователем XxOinvizioNxX, 20 фев 2017.

  1. XxOinvizioNxX

    XxOinvizioNxX Нерд

    *(Текста будет много)
    Жил-был старенький (но рабочий) детский синтезатор:

    [​IMG]
    *(на фото он уже в разобранном состоянии)

    И вот решил сделать бюджетную MIDI - клавиатуру из него!
    Гуглил, я гуглил про это - много чего видел, всякого бреда, который никогда не заработает, и кучу всего бесполезного, типа "подключите каждую клавишу к Arduino Mega...." и т.п. Так что думаю, если кто-то захочет сделать нечто подобное - эта статейка поможет.
    Вытащил из синтезатора клавиатуру (она у меня на 44 клавиши):

    [​IMG]

    Схемы почти всех клавиатур от дешёвых синтезаторов имеют приблизительно одинаковый вид:
    [​IMG]
    В качестве основы я выбрал Nano не только потому что она дешёвая, хотя это тоже положительный момент, просто в ней, в отличие от Uno и некоторых других, 8 аналоговых входов, которые, как известно, можно использовать и как цифровые.
    В качестве считывателя значений клавиш - использовал известную библиотеку Keypad (её можно отрыть во всяких туториалах по подключению клавы 4x4).
    Теперь, о подключении клавы к ардуино:

    ВАЖНО: При подключении необходимо поставить подтягивающие резисторы по 10КОм между +5V ардуино и выводом от строк. (Резисторы подключаются не на землю, а на 5V - это важно!)
    Нарисовал в протеусе, для тех, кто в танке:

    [​IMG]

    Выводы с анодов диодов (смотрите схему клавиатуры) будут колонками для библиотеки, а выводы с кнопок - строками.
    Как-то так:
    Код (C++):
    #include <Keypad.h>
    const byte ROWS = 8; // строки
    const byte COLS = 6; // колонки
    // А это имена для каждой кнопки (можно начинать, конечно с 1, но 48 - это нуль, а так удобнее)
    char keys[ROWS][COLS] = {
      {48,56,64,72,80,88},
      {49,57,65,73,81,89},
      {50,58,66,74,82,90},
      {51,59,67,75,83,91},
      {52,60,68,76,84,92},
      {53,61,69,77,85,93},
      {54,62,70,78,86,94},
      {55,63,71,79,87,95},
    };
    byte rowPins[ROWS] = {2, 3, 4, 5, 6, 7, 8, 9}; //Пины ардуино, на которые подключены выводы от кнопок
    byte colPins[COLS] = {10, 11, A0, A1, A2, A3}; //Пины ардуино, на которые подключены выводы от анодов диодов

    Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
    Можно сделать вывод в сериал порт для проверки правильности подключения. (код будет аналогичным подключению клавы 4x4)

    Но ведь нам нужно не просто включить ноту и тут же её выключить, а пока клавиша зажата её играть, причём с возможностью одновременного нажатия нескольких клавиш (у меня это 6 клавиш) (именно для этого и нужны были резисторы).
    И делается это так: (Код не мой, я его просто доработал)
    Код (C++):
        if (kpd.getKeys())
        {
            for (int i=0; i<LIST_MAX; i++)
            {
                if ( kpd.key[i].stateChanged )
                {
                    switch (kpd.key[i].kstate) {
                        case PRESSED:
                        //Клавишу kpd.key[i].kchar нажали
                    break;
                        case HOLD:
                        //Клавишу kpd.key[i].kchar держат
                    break;
                        case RELEASED:
                        //Клавишу kpd.key[i].kchar отпустили
                    break;
                        case IDLE:
                    break;
                    }
                }
            }
        }
    Теперь об отправки самих миди данных на порт USB:
    Я использовал две программы:
    1. loopMIDI (для скачивания загляните в гугл). Она создаёт виртуальный миди-порт.
    2. Serial MIDI Converter - это и есть конвертер сигнала с ардуино в миди.
    Для отправки ноты надо сделать так:
    Код (C++):
    void noteOn(int cmd, unsigned long pitch, int velocity) {
      Serial.write(cmd);
      Serial.write(pitch);
      Serial.write(velocity);
    }

    //далее сама команда отправки для включения клавиши
    noteOn(0x90, номер клавиши или её код, 0x7F);
    // и для выключения
    noteOn(0x90, номер клавиши или её код, 0x00);
    ВАЖНО: в программа типа фл-студио у каждой клавиши есть номер, именно его и надо прописывать в noteOn. В фл-студио клавиши от 1 (C0) до 131 (B10). Если вы хотите через HEX, то тогда в начало HEX-а надо добавить 0x и далее значение HEX , но этот вариант сложнее и бесполезее. Например, вот команда включения клавиши под номером 41 (F3):
    noteOn(0x90, 41, 0x7F);

    Таким образом код для запуска определённой клавиши будет выглядить как-то так:
    Код (C++):
    // это в лупе (смотрите код выше)
    switch (kpd.key[i].kstate) {
                        case PRESSED:
                        kpress(kpd.key[i].kchar);
                    break;
                        case HOLD:
                    break;
                        case RELEASED:
                        kreleased(kpd.key[i].kchar);
                    break;
                        case IDLE:
                     break;
                    }

    // А вот и сами обработчики:
    void noteOn(int cmd, unsigned long pitch, int velocity) {
      Serial.write(cmd);
      Serial.write(pitch);
      Serial.write(velocity);
    }

    void kpress(char knum) {
        noteOn(0x90, (knum - 7), 0x7F); //Char кнопки в номер клавиши
    }
    void kreleased(char knum) {
        noteOn(0x90, (knum - 7), 0x00); //Char кнопки в номер клавиши
    }
    Ну, в принципе - это уже будет работать (только в сетап поставьте Serial.begin(115200);)
    Теперь нам надо сделать переключение по октавам. В одной октаве всего 12 клавиш (белые + черные)
    Для этого берём пару кнопочек и подключаем их на Analog4 и Analog5 по стандартной схеме с подтягивающим резистором на 10Ком на землю, а также для индикации берём один светодиодик и подключаем через резистор 200-400 Ом на 12 выход ардуино:
    [​IMG]
    (важно, чтобы кнопки были не бомжовые, которые дребезжат, а нормальные, как мои)
    [​IMG]
    (как подключить кнопки и светодиод к ардуино, думаю объяснять не надо...)

    А вот и полный скетч:
    (возможно есть что-то лишнее, т.к. я брал из кучи разных мест и дорабатывал)

    Код (C++):
    #include <Keypad.h>
    uint32_t val = 0;
    const byte ROWS = 8;
    const byte COLS = 6;
    char keys[ROWS][COLS] = {
      {48,56,64,72,80,88},
      {49,57,65,73,81,89},
      {50,58,66,74,82,90},
      {51,59,67,75,83,91},
      {52,60,68,76,84,92},
      {53,61,69,77,85,93},
      {54,62,70,78,86,94},
      {55,63,71,79,87,95},
    };
    byte rowPins[ROWS] = {2, 3, 4, 5, 6, 7, 8, 9};
    byte colPins[COLS] = {10, 11, A0, A1, A2, A3};
    Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

    unsigned long loopCount;
    unsigned long startTime;

    int octave = 0;
    int lstval = 0;
    void setup() {
      pinMode(A5, INPUT);
      pinMode(A4, INPUT);
      pinMode(12, OUTPUT);
        Serial.begin(115200);
        loopCount = 0;
        startTime = millis();
        msg = "";
    }


    void loop() {
      if (digitalRead(A4) == HIGH)
      {
        delay(300);
        if (octave >= 36)
        {
          octave = 0;
        }
          else
          {
        octave = octave + 12;
          }
          alloff();
      }
      if (digitalRead(A5) == HIGH)
      {
        delay(300);
        if (octave <= -36)
        {
          octave = 0;
        }
          else
          {
        octave = octave - 12;
          }
          alloff();
      }
        if (kpd.getKeys())
        {
            for (int i=0; i<LIST_MAX; i++)
            {
                if ( kpd.key[i].stateChanged )
                {
                    switch (kpd.key[i].kstate) {
                        case PRESSED:
                        kpress(kpd.key[i].kchar);
                    break;
                        case HOLD:
                    break;
                        case RELEASED:
                        kreleased(kpd.key[i].kchar);
                    break;
                        case IDLE:
                        break;
                    }
                }
            }
        }
    }
    void noteOn(int cmd, unsigned long pitch, int velocity) {
      Serial.write(cmd);
      Serial.write(pitch);
      Serial.write(velocity);
    }

    void kpress(char knum) {
      digitalWrite(12, HIGH);
        noteOn(0x90, ((knum - 7) + octave), 0x7F);
    }
    void kreleased(char knum) {
        noteOn(0x90, ((knum - 7) + octave), 0x00);
        digitalWrite(12, LOW);
    }
    void alloff() {
      digitalWrite(12, HIGH);
      for (int i=0; i <= 131; i++){
        noteOn(0x90, i, 0x00);
      }
      digitalWrite(12, LOW);
    }
    Программы:
    Для работы всего этого, надо установить две программы (выше сказано).
    После установки открываем loopMIDI и жмакаем на плюсик, чтобы у нас появилось loopMIDI Port 1. После чего loopMIDI можно закрыть (он будет висеть в трее , ну по крайней мере на WIN10 так.)
    Затем подключаем ардуино и заливаем скетч. Открывает программу Serial MIDI Converter и с помощью букв на клаве выбираем наш порт (у меня под буквой A (просто жмём на клавишу A)), затем скорость передачи 115200 (у меня под F), потом B, а затем loopMidi port (у меня это D). Эту программу уже нельзя закрывать. Чтобы исправить скетч - закройте.
    Ну вот и всё! открываем фл студио или ещё что-то, затем в настройках миди выбираем loopMIDI и проверяем! Если будут какие-то вопросы, пишите ниже (админы и модеры, не закрывайте тему). Если всё же тему закроют, мне на мэйл: irialmain@gmail.com

    Вот парочка фоток готового устройства:
    Ещё можно добавить всякие эффекты, кнопки, зная команды midi, дополнять можно очень долго.
    [​IMG]
    [​IMG]
     
  2. serg_admin

    serg_admin Гик

    Спасибо, что опубликовали работу. Знаю людей которые хоте ли бы такое, но не могут.

    Напишу несколько пожеланий:
    - В плату Arduino проводки лучше впаивать вместо пинов, но для этого нужно две платки иметь, одну для разработки одну для внедрения.
    - Для процедуры
    Код (C++):
    void noteOn(int cmd, unsigned long pitch, int velocity)
    Мне кажется важно вписать комментарий в код. Что означает каждый параметр?
    - Если последний параметр <velocity> громкость, то возможно интересно было бы прикрутить регулятор громкости.
    - Можно даже на ультразвуковом дальномере сделать диджейские эффекты когда звуковые затухания регулируются рукой над некой тарелкой, показывали мне такую штуку. Лично меня как-то не впечатлило, но народу вроде нужно.

    - На счет подтягивающих резисторов. Они в контроллере есть. Их можно включить командой digitalWrite(pin, HIGH), для INPUT пинов будет подключен подтягивающий резистор. Это значительно упростит схему.
     
    Последнее редактирование: 20 фев 2017
  3. XxOinvizioNxX

    XxOinvizioNxX Нерд

    Большое спасибо! Да, идей можно очень много придумать. Можно загуглить MIDI - команды и там их будет просто огромнейшее количество, а с дальномером - тоже очень хорошо. Ещё думаю приделать унопки на базовые команды, типа "запись, стоп, старт, плей ну и т.д." Все их буду цеплять на один аналоговый пин ардуино через резисторы. Ещё сейчас в гугле нашёл более продвинутую программу hairless-midiserial . В ней, для заливки кода ничего не надо закрывать, а достаточно просто прервать соединение. Ещё можно приделать экранчик к этому всему, проблема только в количестве пинов...
     
  4. XxOinvizioNxX

    XxOinvizioNxX Нерд

    Также пока планирую обрезать корпус до клавиш, а всю начинку со всеми кнопочками впихнуть в коробочку, и приделать её вплотную к клавишам, типа как у заводских. Хотя, если так думать, то по функционалу самодельная клавиатура может выйти даже лучше, ведь всё ограничивается только фантазией и количеством пинов на ардуино.
     
  5. XxOinvizioNxX

    XxOinvizioNxX Нерд

    А с проводками - исправить можно, но лень выпаивать всё это! Хотя и помпа для отпаивания у меня есть... Вдруг ещё пригодятся ножки, ведь можно, чтобы было красиво купить проводочков мама-мама, их нарезать и использовать.
     
  6. rkit

    rkit Гуру

    Подтягивающие резисторы встроены в ардуино и включаются с помощью pinMode(pin, INPUT_PULLUP)
     
  7. XxOinvizioNxX

    XxOinvizioNxX Нерд

    Спасибо, не знал, попробую, но уже не сегодня.
     
  8. XxOinvizioNxX

    XxOinvizioNxX Нерд

    Только они на 5V или на землю подключены-то?
     
  9. serg_admin

    serg_admin Гик

    В AVR(Arduino) только Pull-up (5V). Как раз то, что нужно.
     
  10. XxOinvizioNxX

    XxOinvizioNxX Нерд

    Ага! Спасибо большое! Буду пробывать
     
  11. Vadok HC'

    Vadok HC' Нуб

    Такой вопрос , если у меня синтезатор на 61 клавишу , мне подойдет Arduino Nano ? Я пока-что не очень разбираюсь в Arduino , так что заранее извиняюсь за глупые вопросы . upload_2017-2-26_18-33-42.png
     
  12. rkit

    rkit Гуру

    Надо в схеме клавиатуры разбираться. Без этого бесполезно.
     
  13. XxOinvizioNxX

    XxOinvizioNxX Нерд

    Я писал как выглядит схема синтезатора. Чтобы определить какие выводы строки, а какие колонки, бери мультиметр и прозванивай выводы от диодов и шлейф от клавиатуры.

    Надо не количество клавиш смотреть, а количество выводов на шлейфе от клавиатуры. Разбери синтезатор, посмотри сколько на шлейфе контактов. У ардуино нано:
    D2-D13 (итого 11) и ещё A0-A7 (ещё 8) (всего 19). Но если я не ошибаюсь, то у нано аналоговые пины можно использовать только как входы, а значит, что на аналоговые ты можешь подключить только выводы с анодов диодов, т.е. колонки (смотри схему). Т.е. если у твоего синтезатора выводов от кнопок (не от диодов! Смотри по схеме) не более 11, а от анодов диодов не более 8 - то подключить ты можешь без всяких расширителей, но учти, если ты займёшь все выводы, то ты не сможешь подключить кнопки для изменения октав. (подсказка: несколько кнопок можно подключить на один аналоговый вход).
    ВЫВОД:
    Чтобы подключить твой синтезатор вместе с двумя кнопками для изменения октав, у тебя должно быть на плате выводов от кнопок не более 11, а от анодов диодов не более 7.

    [​IMG]

    Сомневаюсь что написал понятно, просто торопился очень. Если что-то не понятно, пиши. А ещё, желательно, пришли фотку шлейфа.
     
  14. Vadok HC'

    Vadok HC' Нуб

    Спасибо большое , на след выходных прозвоню все , буду пробовать , стаття очень помогла
     
  15. XxOinvizioNxX

    XxOinvizioNxX Нерд

    Ну я вообще не прозванивал, а замыкал выводы на клаве при включённом синтезаторе и слушал звук. Но лучше так не делать, тем более, у вас синтезатор нормальный, а ни как у меня :D . Но если вам не жалко плату синтезатора, то можно и замыкать как я. (хотя возможно, и ничего не будет)

    Спасибо! ;)
     
  16. XxOinvizioNxX

    XxOinvizioNxX Нерд

    Ещё есть всякие расширители выводов, сдвиговые регистры и т.д. Дело с ними и библиотекой Keypad не имел, но думаю в гугле что-то есть. (хотя не знаю)
     
  17. Vadok HC'

    Vadok HC' Нуб

    При вводе вашего скетча выдает ошибку "fatal error: Keypad.h: No such file or directory"

    Уже нашел в чем проблема , нужно скачать библиотеку keypad
    Но все равно находит кучу ошибок типа :

    sketch_mar02a:6: error: stray '\240' in program
    sketch_mar02a:7: error: stray '\302' in program
    В {49,57,65,73,81,89},
    ^
    sketch_mar02a:7: error: stray '\240' in program
    sketch_mar02a:8: error: stray '\302' in program
    В {50,58,66,74,82,90},
     
    Последнее редактирование: 3 мар 2017
  18. XxOinvizioNxX

    XxOinvizioNxX Нерд

    Значит скачал или поставил криво. С официального сайта качай, а распаковывать в папку libraries в корне ардуино. И убедить, что у тебя папка с либой называется также, как её 2 файла внутри Keypad.h и Keypad.cpp. Ошибок быть не должно, по крайней мере, если устанавливать всё с официальных ресурсов.
    {50,58,66,74,82,90} - эти цифры - это номера символов char.
     
  19. Vadok HC'

    Vadok HC' Нуб

    Может ошибка из-за того что я набирал не вручную , а методом Копировать-Вставить ?

    Через MariaMole все получилось
     
    Последнее редактирование: 3 мар 2017
  20. ostrov

    ostrov Гуру

    У меня такой Касио 47 настоящий есть! А когда видео? Хочу посмотреть реинкарнацию.

    Кстати, хочу midi клаву купить, чтобы не только физика, но и лирика творчеством выход находила! Но уже голову сломал какую выбрать. Все такое вкусное. )
     
    Последнее редактирование: 3 мар 2017
    ИгорьК нравится это.