Миди на Teensy4.0

Здравствуйте! Помогите решить проблему. Написан скетч для мидиконтроллера на Teensy4.0. К ней подключено 18 фэйдеров, 4 энкодера и матрица кнопок. Скетч состоит из файлов .ino .cpp .h. Фэйдеры и энкодеры находятся в файле .ino , и выходят в usbmidi нормально, а кнопки находятся в файле .cpp и в usbmidi никак не хотят выходить. Хотя они работают и в serial monitore их видно. Скорее всего недостаток какой то строки в файле .срр. Спасибо($) откликнувшимся!

что бы это значило?

и что, даже это предположение не навело вас на мысль, что надо показывать код???

Здравствуйте! На форуме впервые! Конечно я понял ошибку. Но код на домашнем компе. Выложу только вечером, сейчас на работе. Спасибо за отклик!

После загрузки кода, на выходе Usbmidi (смотрю в Midi View) сообщения от потенциометров и энкодеров приходят. От кнопок нет, хотя , в Serial Monitor видно, что срабатывают. Извините, что своими словами

тогда вечером и надо было тему создавать.

Без кода обсуждать нечего.

Прошу прощения за слова “Скорее всего недостаток какой то строки в файле .срр.” Сделал вывод за Вас. Ведь совершенно не уверен, что дело в этом

sketch_jan28a.ino

#include <Encoder.h>
#include <MIDI.h>
#include "matrix_buttons.h"
#include <MIDIUSB.h>

// === Распределение пинов ===
const int muxSelectPins[] = {14, 15, 16, 17}; // Пины для селекторов мультиплексора
const int muxOutputPin = 18; // Аналоговый выход мультиплексора
Encoder encoders[] = {
  Encoder(10, 11),
  Encoder(0, 1),
  Encoder(22, 23),
  Encoder(24, 25)
};

const int additionalPotPins[] = {34, 35}; // Новые потенциометры
int additionalPotValues[2] = {0}; // Значения потенциометров
int lastAdditionalPotValues[2] = {0};

// === Переменные ===
int potentiometerValues[16] = {0}; // Изменено на 16
int lastPotentiometerValues[16] = {0}; // Изменено на 16
long encoderValues[4] = {0};
long lastEncoderValues[4] = {0};

// === Настройки ===
const int potentiometerThreshold = 2; // Гистерезис для потенциометров
const int encoderStep = 4;            // Шаг для энкодеров

void setup() {
  // Настройка пинов селекторов мультиплексора
  for (int i = 0; i < 4; i++) {
    pinMode(muxSelectPins[i], OUTPUT);
  }
  
  // Настройка пинов для дополнительных потенциометров
  for (int i = 0; i < 2; i++) {
    pinMode(additionalPotPins[i], INPUT);
  }
  
  // Инициализация Serial для отладки
  Serial.begin(9600);

  // Инициализация матрицы кнопок
  setupMatrixButtons();

  // Инициализация MIDI
  usbMIDI.begin();
}

void loop() {
  // --- Потенциометры (через мультиплексор) ---
  for (int i = 0; i < 16; i++) {
    // Устанавливаем селекторы для мультиплексора
    for (int j = 0; j < 4; j++) {
      digitalWrite(muxSelectPins[j], (i >> j) & 1);
    }

    // Читаем значение с выходного пина мультиплексора
    int value = analogRead(muxOutputPin) / 8; // Приводим к диапазону 0-127
    if (abs(value - lastPotentiometerValues[i]) > potentiometerThreshold) { // Проверяем порог
      usbMIDI.sendControlChange(46 + i, value, 1); // CC №1-16 для потенциометров
      lastPotentiometerValues[i] = value;
    }
  }

  // --- Новые потенциометры (прямое подключение) ---
  for (int i = 0; i < 2; i++) {
    int value = analogRead(additionalPotPins[i]) / 8; // Приводим к диапазону 0-127
    if (abs(value - lastAdditionalPotValues[i]) > potentiometerThreshold) { // Проверяем порог
      usbMIDI.sendControlChange(62 + i, value, 1); // CC №17-18 для новых потенциометров
      lastAdditionalPotValues[i] = value;
    }
  }

  // --- Матрица кнопок ---
  readMatrixButtons();
  // printMatrixButtons(); // Для отладки (убрать в финальной версии)

  // --- Энкодеры ---
  for (int i = 0; i < 4; i++) {
    long value = encoders[i].read();
    if (abs(value - lastEncoderValues[i]) >= encoderStep) { // Проверяем шаг
      if (value > lastEncoderValues[i]) {
        usbMIDI.sendControlChange(20 + i, 1, 1); // Увеличение CC №9-12
      } else {
        usbMIDI.sendControlChange(20 + i, 127, 1); // Уменьшение CC №9-12
      }
      lastEncoderValues[i] = value;
    }
  }

  // Убери `delay(100)` в финальной версии, если хочешь быструю реакцию.
  delay(10); // Пауза для отладки
}

matrix_buttons.cpp

#include "matrix_buttons.h"
#include <Arduino.h>
#include <MIDIUSB.h>

// Определяем константы и глобальные переменные


const int numRows = 3; // Количество строк
const int numCols = 4; // Количество столбцов

const int rowPins[numRows] = {19, 20, 21}; // Пины строк
const int colPins[numCols] = {2, 3, 4, 5}; // Пины столбцов

bool matrixButtons[3][4] = {{false}}; // Состояние кнопок (изначально false)

// Определяем функции
void setupMatrixButtons() {
    Serial.println("Инициализация матрицы кнопок с переключением INPUT/OUTPUT...");

    // Настраиваем строки и столбцы как входы с подтяжкой
    for (int i = 0; i < numRows; i++) {
        pinMode(rowPins[i], INPUT_PULLUP);
    }
    for (int j = 0; j < numCols; j++) {
        pinMode(colPins[j], INPUT_PULLUP);
    }

    Serial.println("Матрица кнопок инициализирована.");
}

void readMatrixButtons() {
    // Считываем состояние кнопок в матрице
    for (int i = 0; i < numRows; i++) {
        // Активируем строку: переводим её в OUTPUT и ставим LOW
        pinMode(rowPins[i], OUTPUT);
        digitalWrite(rowPins[i], LOW);
        delayMicroseconds(5); // Даем сигналу стабилизироваться

        for (int j = 0; j < numCols; j++) {
            // Читаем состояние столбца
            bool currentState = !digitalRead(colPins[j]);

            // Проверяем состояние кнопки
            if (currentState && !matrixButtons[i][j]) {
                matrixButtons[i][j] = true;
                Serial.print("Нажата кнопка: строка ");
                Serial.print(i);
                Serial.print(", столбец ");
                Serial.println(j);
            } else if (!currentState && matrixButtons[i][j]) {
                matrixButtons[i][j] = false;
            }
        }

        // Возвращаем строку в режим INPUT
        pinMode(rowPins[i], INPUT_PULLUP);
    }
}




void printMatrixButtons() {
    // Выводим состояние кнопок в Serial
    Serial.println("Состояние кнопок:");
    for (int i = 0; i < numRows; i++) {
        for (int j = 0; j < numCols; j++) {
            Serial.print(matrixButtons[i][j]);
            Serial.print(" ");
        }
        Serial.println();
    }
}
// Отправка MIDI Note On
void sendNoteOn(byte pitch, byte velocity) {
    midiEventPacket_t noteOn = {0x09, 0x90, pitch, velocity};
    MidiUSB.sendMIDI(noteOn);
    MidiUSB.flush();
}

// Отправка MIDI Note Off
void sendNoteOff(byte pitch, byte velocity) {
    midiEventPacket_t noteOff = {0x08, 0x80, pitch, velocity};
    MidiUSB.sendMIDI(noteOff);
    MidiUSB.flush();
}

matrix_buttons.h

#ifndef MATRIXBUTTONSH // Начало защиты от повторного включения
#define MATRIXBUTTONSH

#include <Arduino.h> // Подключаем Arduino.h для byte и других типов

// Объявляем константы и глобальные переменные
extern const int numRows;
extern const int numCols;
extern const int rowPins[];
extern const int colPins[];
extern bool matrixButtons[3][4]; // Пример для матрицы 6x9

// Объявляем функции
void setupMatrixButtons();
void readMatrixButtons();
void printMatrixButtons();

#endif // MATRIXBUTTONSH

Потенциометры 16 шт подключены через мультиплексор 74hc4067 S0, S1, S2, S3 - 14, 15, 16, 17. SIG- 18. EN-GND. Два потенциометра подключены напрямую на пины 34, 35. Энкодеры и матрица кнопок подключены напрямую на пины, в коде видно

Похоже вы совсем совсем не понимаете работу программы, если это так, то советы скорее всего не помогут.
По существу.
Кнопки у вас и не пытаются ничего отправлять в MIDI.
Не знаю, какая команда нужна при нажатии отпускании, но скорее всего надо добавить после
matrixButtons[i][j] = true;
вызов
sendNoteOn(byte pitch, byte velocity); где pitch определяется нажатой кнопкой

а после
matrixButtons[i][j] = false;
вызов
sendNoteOff(byte pitch, byte velocity);

Да! Не сильно разбираюсь еще. И написал в соответствующей ветке. Есть желание разбираться дальше и спрашивать буду если что, но в других ветках. А пока прошу помощь в исправлении скетча. То что Вы предложили не сработало, компилятор выдал много ошибок

Покажите новую версию кода, которая у вас получилась после исправления по совету @Upper .
Если компилятор дает ошибки - приведите их полностью (текстом)

PS Вы, похожеЮ так и не разобрались, как общаться на этом форуме. Любые вопросы по программе - только с кодом!

Добрый день! Переписали код в один скетч. Кнопки заработали но… кроме одной. угловая кнопка на строке ( по индексу) r0 пин 19, и столбце c0 пин 2. Пины все исправны, менял местами, все рабочие, кнопки то же все рабочие. Как бы не конфигурировал матрицу- не работает именно эта кнопка r0-c0. Спрашивал у GPT и у DeepSeek без результата, предлагают одно и то же- проверить пины, дребезг, поменять ноту. ничего не помогает. Вот код

#include <Encoder.h>
#include <MIDI.h>
#include <Bounce2.h>

// ==================== Конфигурация пинов ====================
// Мультиплексор 74HC4067
const int muxSelectPins[] = {14, 15, 16, 17}; // S0-S3
const int muxOutputPin = 18; // Analog input

// Прямые потенциометры
const int directPots[] = {34, 35}; // A8, A9

// Энкодеры
Encoder encoders[] = {
  Encoder(10, 11),  // Enc1
  Encoder(0, 1),    // Enc2 (не использовать если занят Serial)
  Encoder(22, 23),  // Enc3
  Encoder(24, 25)   // Enc4
};

// Матрица кнопок 3x4
const int rowPins[] = {19, 20, 21};    // Rows
const int colPins[] = {2, 3, 4, 5};    // Columns
Bounce buttonDebouncers[3][4];         // Антидребезг

// ==================== Настройки MIDI ====================
const int MIDI_CHANNEL = 1;
const int NOTE_BASE = 36; // Первая нота (C2)

// ==================== Глобальные переменные ====================
// Потенциометры
int lastMuxValues[16] = {0};
int lastDirectPotValues[2] = {0};

// Энкодеры
long lastEncoderPositions[4] = {0};

// ==================== Инициализация ====================
void setup() {
  // Настройка мультиплексора
  for(int i = 0; i < 4; i++) pinMode(muxSelectPins[i], OUTPUT);
  
  // Настройка матрицы кнопок
  for(int r = 0; r < 3; r++) {
    pinMode(rowPins[r], OUTPUT);
    digitalWrite(rowPins[r], HIGH);
    for(int c = 0; c < 4; c++) {
      buttonDebouncers[r][c].attach(colPins[c], INPUT_PULLUP);
      buttonDebouncers[r][c].interval(10);
    }
  }

  usbMIDI.begin();
}

// ==================== Главный цикл ====================
void loop() {
  static unsigned long lastScan = 0;
  
  if(millis() - lastScan >= 10) { // Опрос каждые 10 мс
    lastScan = millis();
    
    readMultiplexer();    // 16 потенциометров
    readDirectPots();     // 2 прямых потенциометра
    readEncoders();       // 4 энкодера
    readMatrixButtons();  // 12 кнопок
  }
}

// ==================== Функции чтения ====================
void readMultiplexer() {
  for(int ch = 0; ch < 16; ch++) {
    // Выбор канала мультиплексора
    for(int bit = 0; bit < 4; bit++) 
      digitalWrite(muxSelectPins[bit], (ch >> bit) & 1);
    
    int value = analogRead(muxOutputPin) / 8;
    if(abs(value - lastMuxValues[ch]) > 2) {
      usbMIDI.sendControlChange(ch, value, MIDI_CHANNEL);
      lastMuxValues[ch] = value;
    }
  }
}

void readDirectPots() {
  for(int i = 0; i < 2; i++) {
    int value = analogRead(directPots[i]) / 8;
    if(abs(value - lastDirectPotValues[i]) > 2) {
      usbMIDI.sendControlChange(16 + i, value, MIDI_CHANNEL);
      lastDirectPotValues[i] = value;
    }
  }
}

void readEncoders() {
  for(int i = 0; i < 4; i++) {
    long newPos = encoders[i].read();
    long delta = (newPos - lastEncoderPositions[i]) / 4;
    
    if(delta != 0) {
      usbMIDI.sendControlChange(
        20 + i, 
        (delta > 0) ? 1 : 127, 
        MIDI_CHANNEL
      );
      lastEncoderPositions[i] = newPos;
    }
  }
}

void readMatrixButtons() {
  for(int r = 0; r < 3; r++) {
    digitalWrite(rowPins[r], LOW);
    delayMicroseconds(5);
    
    for(int c = 0; c < 4; c++) {
      buttonDebouncers[r][c].update();
      
      if(buttonDebouncers[r][c].fell()) {
        usbMIDI.sendNoteOn(NOTE_BASE + (r * 4) + c, 127, MIDI_CHANNEL);
      }
      if(buttonDebouncers[r][c].rose()) {
        usbMIDI.sendNoteOff(NOTE_BASE + (r * 4) + c, 0, MIDI_CHANNEL);
      }
    }
    
    digitalWrite(rowPins[r], HIGH);
  }
}

Не уверен, что это имеет отношение к кнопке, но в этом цикле у вас явно проблема со временем:

Опрос 16 + 2 потенциометров, 4х энкодеров и 12 кнопок никак не уложится в 10 мс. У вас дебонс на каждую кнопку уже по 10мс настроен, насколько я вижу. То есть только на кнопки 120мс уйдет

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

Если предположить, что это потому, что она опрашивается первой, то:
Можно пробовать увеличить время delayMicroseconds(5); например до 20.
Можно попробовать поменять порядок сканирования
for(int r = 2; r > =0; r–) {

Можно добавить вывод в Serial, что именно с нее читается. И результат дебаунсера
Можно добавить delay для этого варианта и мерить мультиметром.

Не по поводу 0:0
Времена про которые писал МММ , по моему у вас все нормально.

поясни.
Рассуждаю так - 10мс для дебонса это минимум, даже если дебонсить все кнопки параллельно, все равно налицо дефицит времени

Спасибо за участие! То есть Вы предлагаете начать опрос кнопок со второй строки и, если неработающая кнопка сместится на эту строку, то уже точно будет понятно, что ошибка в опросе строк?

Просто это самое простое, что можно проверить (на авось, но это мало вероятно).
Более вероятно поможет разобраться вывод в сериал, и измерение мультиметром.

Добавлено позже.
А на первом варианте, который не выводил кнопки в MIDI а выводил в Serial, там кнопка 0:0 работала?

А вот сейчас проверил! Оказывается на том первом коде тоже не все кнопки срабатывают, включая и угловую кнопку.