Arduino — это не только про соединение проводов и написание скетчей, но и про умение подружить железки, которые, казалось бы, созданы друг для друга, но упорно не желают работать вместе. Эта статья — история одной такой дружбы. Рассматривается решение, написанное и протестированное на основе товара «Умный дом на базе Arduino. Большой набор + книга», который вы можете приобрести в нашем Интернет-магазине.
Вступление
В рамках «импортозамещения» мы решили модернизировать наш популярный набор «Умный дом на базе Arduino». Причины были чисто практические:
Замена Bluetooth-модуля HM-10 на HC-06, т.к. модуль HM-10 теперь отсутствует в «Едином реестре нотификаций о характеристиках шифровальных (криптографических) средств и товаров», поэтому его ввоз в Россию требует оформления специальных сертификатов. Это долго и дорого. А модуль HC-06 в реестре пока имеется и есть у нас в достаточном количестве. А при желании, пользователь может купить HM-10 самостоятельно в магазине или на AliExpress — их продажа не запрещена, проблемы с ввозом оптовых партий.
Замена приложения RemoteXY на Virtuino. RemoteXY невозможно купить в Google Play из России из-за блокировки платежей, а возможности бесплатной версии стали существенно ограничены.

Казалось бы, простая замена компонентов. Но когда мы начали тестировать все эксперименты из набора, нас ждал сюрприз: При одновременной работе RFID-модуля RC522 и светодиодной матрицы 8×8 на MAX7219 матрица зависала при поднесении карточки RFID. По отдельности оба устройства работали идеально, но вместе — отказывались дружить.


Самое интересное: этот же выставочный макет успешно работал весной прошлого года и с тех пор мы его не включали. Железо осталось тем же — те же модули, та же Arduino, те же провода. Что же изменилось? Мы обновили Arduino IDE до последней версии, а вместе с ней — обновились и библиотеки. Нашли информацию, что библиотека по управлению матрицей — LedControl.h (версия 1.06) — не менялась с 2015 года (https://wayoda.github.io/LedControl/index.html), а вот библиотека SPI, входящая в состав ядра Arduino, вполне могла измениться в сторону улучшения взаимодействия устройств SPI. Скорее всего, именно обновление SPI-библиотеки привело к тому, что после работы RFID пины оставались в состоянии, непригодном для работы матрицы.
Мы перерыли весь интернет в поисках решения. Вопросы по этой теме встречаются на форумах, но внятных ответов мы не нашли. Кто-то советовал ставить дополнительные конденсаторы, кто-то — переходить на I2C, кто-то — разводить питание. Ничего не помогало. Пришлось разбираться самостоятельно.
Почему так происходит, и как это исправить — читайте далее.
В чём проблема?
Давайте посмотрим на стандартную схему подключения в наборе (рис. 4, табл. 1).

Пин Arduino | RFID-модуль RC522 | Матрица на MAX7219 | Сигнал SPI | Описание |
D9 | RST | — | RST (Reset) | Сброс модуля. Не является сигналом SPI, но необходим для инициализации RC522. При подаче низкого уровня (LOW) модуль сбрасывается. |
D10 | SDA | CS | SS (Slave Select) | Выбор ведомого устройства. Когда сигнал низкий (LOW), модуль понимает, что к нему обращаются. Может обозначаться как SS, CS (Chip Select), SDA (на RC522). |
D11 | MOSI | DIN | MOSI (Master Out Slave In) | Выход мастера, вход ведомого. По этой линии Arduino передаёт данные на устройство. Может обозначаться как MOSI, DI (Data In), DIN, SI (Serial In). |
D13 | SCK | CLK | SCK (Serial Clock) | Тактовый сигнал (импульсы синхронизации). Генерируется мастером (Arduino). Все устройства на шине SPI слушают этот сигнал. Может обозначаться как: SCK, SCLK, CLK. |
D12 | MISO | — | MISO (Master In Slave Out) | Вход мастера, выход ведомого. По этой линии устройство передаёт данные в Arduino. Для матрицы MAX7219 этот сигнал не используется (матрица только принимает данные, но не отправляет). Может обозначаться как: MISO, DO (Data Out), DON, SO (Serial Out). |
Таблица 1. Назначение контактов при подключении подключение
модуля RFID и светодиодной матрицы 8x8 MAX7219 к Arduino Uno
Обратите внимание: оба устройства используют одни и те же линии D11 (MOSI/DIN) и D13 (SCK/CLK). Но такое подключение (общие линии MOSI и SCK, раздельные линии CS/SS) является абсолютно стандартным для организации шины SPI с несколькими ведомыми устройствами. Как подробно описано в статье «Введение в интерфейс SPI» (из книги Т. Иго “ Умные вещи: Arduino, датчики и сети для связи устройств”) на нашем сайте BHV (https://bhv.ru/wikibook/vvedenie-v-interfejs-spi), ��нтерфейс SPI позволяет подключить к шине несколько устройств под управлением одного главного контроллера, объединяя все линии, кроме выбора ведомого (CS/SS). Это нормальная и правильная схема включения (рис. 5).

Если всё правильно, то почему наша схема не работает?
Казалось бы, подключение выполнено по всем правилам SPI-шины: линии MOSI и SCK общие, CS раздельные. Именно так рекомендуется подключать несколько устройств SPI. Так почему же матрица зависает?
Всё дело в том, КАК именно устройства используют эти линии. Тут и оказалась зарыта «та самая собака», которая не давала одновременно работать этим двум устройствам RFID-модуль RC522 использует аппаратный SPI, а MAX7219 — программный (см. табл. 2).
Устройство | Тип SPI | Как работает |
RFID-модуль RC522 | Аппаратный | Управляется специальным оборудованием внутри микроконтроллера. После инициализации аппаратный SPI «захватывает» пины D11 (MOSI) и D13 (SCK) — они перестают подчиняться обычным командам |
Матрица на MAX7219 | Программный | Библиотека LedControl управляет пинами вручную через |
Таблица 2. Аппаратный и программный SPI
В этом и заключается конфликт. Пока RFID молчит, матрица прекрасно работает — пины свободны. Но как только RFID выполняет чтение карты, его библиотека настраивает аппаратный SPI, и пины D11, D13 переходят под аппаратное управление. Матрица продолжает «стучаться» в эти пины через digitalWrite, но аппаратный SPI игнорирует её команды. Результат — полное зависание изображения.
Таким образом, проблема не в схеме, а в конфликте двух разных способов управления одними и теми же пинами.
А что, если перейти на I2C или UART?
Согласно даташиту микросхема MFRC522, установленная на модуле RFID поддерживает I2C и UART (рис. 6)! Может, просто переключить её в этот режим и забыть о конфликте?

Технически это возможно, но, как говорится, дьявол в деталях. На форуме Arduino народ уже давно обсуждает эту тему. Результаты экспериментов — настоящий коктейль из энтузиазма и разочарования.
Что предлагают энтузиасты:
Нужно физически разорвать две дорожки на плате (к выводам 1 и 32 микросхемы)
Затем подпаять перемычки, чтобы задать нужный режим
Некоторые даже умудряются подсунуть кусочек каптоновой ленты под выводы микросхемы перед пайкой, чтобы не резать дорожки
Что получается на практике:
У кого‑то действительно получается заставить модуль работать по I2C
У других модуль просто перестаёт определяться
Третьи возвращают всё обратно и SPI снова работает
Некоторые подозревают, что на китайских модулях стоят поддельные микросхемы, которые вообще не поддерживают другие режимы
Наш вердикт: даже если вы готовы рискнуть и имеете навыки микроскопической пайки, успех не гарантирован. А для обычного пользователя Arduino, который просто хочет собрать умный дом, этот путь превращается в эпопею с непредсказуемым финалом.
Поэтому мы оставили эксперименты с I2C смелым энтузиастам, а сами пошли искать решение в родной для этих модулей среде — мире SPI. И, как видите, нашли!
Простое решение: развести по разным пинам
Самое очевидное решение — разнести устройства по разным пинам. Например, матрицу можно подключить на другие цифровые выходы:
Назначение | Обычный пин | Новый пин |
DIN (данные) | 11 | 7 |
CLK (тактирование) | 13 | 6 |
CS (выбор кристалла) | 8 | 8 (можно оставить) |
Вот как это выглядит на схеме подключения (рис. 7).

Листинг 1. Код для такого подключения:
// Новые пины для матрицы #define MATRIX_DIN 7 #define MATRIX_CLK 6 #define MATRIX_CS 8 LedControl matrix = LedControl(MATRIX_DIN, MATRIX_CLK, MATRIX_CS, 1);
И всё будет работать! Но есть один недостаток — мы занимаем два дополнительных пина. В проекте «Умный дом» пины и так все заняты, а тут ещё теряем ценный ШИМ-выход D6, который можно было бы использовать для сервопривода, открывающего дверь.
Элегантное решение: SPI.end() приходит на помощь
Для отладки мы написали специальный тестовый скетч, который делает всего две вещи:
1. Тестирует матрицу — при запуске зажигает на матрице все светодиоды, а затем последовательно показывает стрелку, счастливый и нейтральный смайлы. Это позволяет убедиться, что матрица работает исправно до любого взаимодействия с RFID.
2. Реагирует на RFID — при поднесении карточки в последовательный порт выводится сообщение «Доступ разрешен» (или «Доступ запрещен»), а на матрице должна загореться стрелка, указывающая путь к двери. После паузы матрица возвращается к нейтральному смайлу.
Этот минималистичный скетч помог нам изолировать проблему и убедиться, что дело именно в конфликте SPI, а не в других частях основного проекта.
После долгих экспериментов мы нашли изящное решение. Оказывается, достаточно перед каждым обращением к матрице полностью отключать аппаратный SPI, и больше его не включать!
Секрет в функции SPI.end(). Она не только отключает SPI, но и переводит пины в исходное состояние. После этого матрицу нужно заново проинициализировать — и она работает как ни в чём не бывало.
Важный момент: после SPI.end() мы не вызываем SPI.begin()!
Библиотека MFRC522 при следующем обращении к RFID сама проверит состояние SPI и при необходимости включит его. Это экономит память и делает код стабильнее.
Алгоритм простой:
1. RFID обнаружил карту и прочитал данные.
2. Выключаем SPI: SPI.end();
3. Делаем небольшую задержку.
4. Заново настраиваем пины матрицы как выходы.
5. Переинициализируем матрицу (shutdown(false), setIntensity, clearDisplay).
6. Показываем нужное изображение (например, стрелку).
7. НЕ включаем SPI обратно — библиотека RFID сделает это сама при следующем вызове.
Код, который работает на 100%
Вот финальная версия, которую мы используем в проекте. Все пины остаются стандартными, ничего переподключать не нужно:
Листинг 2. Файл конфигурации config.h
// Пины остаются стандартными! #define DIN_PIN 11 #define CLK_PIN 13 #define CS_PIN 8 #define SS_PIN 10 #define RST_PIN 9 // Изображения const byte smile_happy[8] PROGMEM = { B00000000, B00011000, B00100100, B01000010, B00000000, B00000000, B00100100, B00000000 }; const byte smile_normal[8] PROGMEM = { B00000000, B00000000, B00000000, B01111110, B00000000, B00000000, B00100100, B00000000 }; // Стрелка, указывающая на дверь const byte arrow[8] PROGMEM = { 0b00111111, 0b00011111, 0b00011111, 0b00111111, 0b01111111, 0b11111001, 0b01110000, 0b00100000 }; // Код вашей карточки RFID const String allowedCards[] = {"7C 36 39 A5", "D2 07 A9 1B"}; const int cardCount = 2;
Листинг 3. Основной код
#include <SPI.h> #include "LedControl.h" #include "MFRC522.h" #include "config.h" LedControl matrix = LedControl(DIN_PIN, CLK_PIN, CS_PIN, 1); MFRC522 rfid(SS_PIN, RST_PIN); void setup() { Serial.begin(9600); // Инициализация матрицы matrix.shutdown(0, false); matrix.setIntensity(0, 8); matrix.clearDisplay(0); // Тест матрицы (по желанию) for (int row = 0; row < 8; row++) matrix.setRow(0, row, 0xFF); delay(500); matrix.clearDisplay(0); showSmile(1); // Инициализация RFID SPI.begin(); rfid.PCD_Init(); } void loop() { if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) { // Читаем UID карты String uid = ""; for (byte i = 0; i < rfid.uid.size; i++) { uid += (rfid.uid.uidByte[i] < 0x10 ? " 0" : " "); uid += String(rfid.uid.uidByte[i], HEX); } uid.toUpperCase(); uid = uid.substring(1); bool access = false; for (int i = 0; i < cardCount; i++) { if (uid == allowedCards[i]) access = true; } rfid.PICC_HaltA(); // *** ВОЛШЕБСТВО: отключаем SPI перед работой с матрицей *** SPI.end(); delay(5); // Переинициализируем пины матрицы pinMode(DIN_PIN, OUTPUT); pinMode(CLK_PIN, OUTPUT); pinMode(CS_PIN, OUTPUT); digitalWrite(CS_PIN, HIGH); matrix.shutdown(0, false); matrix.setIntensity(0, 8); matrix.clearDisplay(0); if (access) { Serial.println("Доступ разрешен"); // Показываем стрелку for (int r = 0; r < 8; r++) matrix.setRow(0, r, pgm_read_byte(&arrow[r])); delay(2000); // Здесь можно открыть дверь сервоприводом // ... } else { Serial.println("Доступ запрещен"); for (int i = 0; i < 3; i++) { matrix.clearDisplay(0); delay(200); showSmile(1); delay(200); } } // Возвращаем обычный смайл showSmile(1); // *** ВАЖНО: НЕ вызываем SPI.begin() здесь! *** // Библиотека MFRC522 сама включит SPI при следующем обращении } delay(100); } void showSmile(int type) { const byte* s = (type == 0) ? smile_happy : smile_normal; for (int r = 0; r < 8; r++) matrix.setRow(0, r, pgm_read_byte(&s[r])); }
Почему это работает?
Весь секрет в функции SPI.end(), которая:
1. Отключает аппаратный SPI.
2. Сбрасывает регистры SPI в исходное состояние.
3. Переводит пины в обычный режим (не периферия).
После этого матрица получает «чистые» пины и может работать с ними через программный SPI сколько угодно. А библиотека MFRC522 «достаточно умна», чтобы при следующем вызове PICC_IsNewCardPresent() проверить состояние SPI и, если нужно, включить его снова. Поэтому явный вызов SPI.begin() не требуется и даже вреден, т.к. приводит к зависанию матрицы.
Преимущества этого решения
Не требует переподключения проводов — все остаётся на стандартных пинах.
Экономит пины — не нужно занимать дополнительные выходы.
Сохраняет ШИМ-пины — D6 и D9 остаются свободными для сервоприводов.
Простота — всего несколько строк кода решают проблему.
Стабильность — проверено сотнями циклов открытия двери.
Экономия памяти — не вызываем лишний раз
SPI.begin().Проверено на практике — то, чего не нашли в интернете, нашли сами.
Заключение
Если вы столкнулись с тем, что в вашем проекте RFID и матрица MAX7219 не хотят работать вместе — не спешите перепаивать провода, заказывать другие модули или переписывать программный код. Мы перерыли весь интернет в поисках решения, но внятных ответов не нашли. Пришлось разбираться самим. И вот к чему мы пришли после долгих экспериментов.
Просто используйте SPI.end() перед работой с матрицей и не вызывайте SPI.begin() после. Это простое и элегантное решение, которое мы нашли методом проб и ошибок, и теперь делимся им с вами.
Важное замечание:
Мы не испытывали десятки модулей и матрицы, а только «подружили» те, которые были у нас под рукой и перестали работать после обновления IDE. Поэтому не можем на 100% гарантировать, что с вашими модулями проблем не будет — производители могут использовать разные версии микросхем и компонентов. Однако уверенность есть, и очень надеемся, что наше решение поможет и вам!
Наш модернизированный набор «Умный дом на базе Arduino. Большой набор + книга» с HC-06 и Virtuino отлично работает, дверь открывается по RFID-картам и по команде со смартфона, на матрице загорается стрелка, а студенты не тратят часы на поиск неочевидной проблемы.

Надеемся, эта статья сэкономит ваше время и нервы тоже!
Будем благодарны за ваши идеи и решения! Если вы нашли другой способ подружить эти устройства (например, по протоколу I2С или UART), или столкнулись с похожей проблемой — поделитесь опытом в комментариях. Вместе мы сможем найти ещё более элегантные решения.
Авторы выражают благодарность сообществу Arduino-энтузиастов, которые не боятся экспериментировать и делиться находками.
