Ардуино-кубик с LED-матрицей: бросок по встряхиванию
Компоненты и материалы
| Компонент | Количество |
|---|---|
| Arduino Nano R3 | 1 |
| LED-матрица 8×8 с драйвером MAX7219 | 1 |
| Модуль наклонного датчика (Tilt switch) | td>1|
| Тумблер (Slide Switch) | 1 |
| Повышающий DC-DC преобразователь MT3608 (2–24 В → 5/9/12/28 В) | 1 |
Необходимые инструменты
- Паяльник
- Клейкий пистолет
Программное обеспечение и сервисы
- Arduino IDE
- Autodesk Fusion 360 (для 3D-моделирования корпуса)
О проекте
Примерно год назад мой сын и я собрали электронный кубик на Arduino с несколькими светодиодами. Это по-прежнему идеальный проект для начинающих: понятная функция, простое понимание микроконтроллеров, базовой электроники и программирования.
Версия 2.0
Новый кубик делает шаг вперёд, объединяя несколько направлений. Сегодня для почти любой задачи существуют готовые модули: датчики, кнопки, индикаторы — всё на платах с удобными выводами. Доступные 3D-принтеры (от ~140 €) позволяют создавать любые крепления и корпуса. Наш кубик 2.0 — пример такого подхода.
Датчики наклона (Tilt Sensors)
Бросок реализован иначе: внутри работает Arduino Nano (по соображениям компактности). Кнопки нет — кубик бросают, встряхивая его целиком. Для детектирования встряхивания используются два наклонных датчика, установленных под углом 90° друг к другу. Внутри стеклянной колбы шарик замыкает контакты при движении — этот кратковременный сигнал используется как триггер. Два датчика гарантируют надежное срабатывание при встряхивании в любом направлении.
Прерывания (Interrupts)
Опрос пинов датчиков в основном цикле может пропустить короткие импульсы. Решение — аппаратные прерывания (attachInterrupt) на пинах D2 и D3 Arduino. При срабатывании датчика вызывается подпрограмма, увеличивающая счётчик встряхиваний.
Индикация: LED-матрица 8×8 с MAX7219
Вместо 7 отдельных светодиодов используется компактная матрица 8×8 с драйвером MAX7219. Она дешёва, проста в подключении (SPI-подобный интерфейс) и программировании. При сборке модуля важно правильно ориентировать матрицу в гнезде — при ошибке её достаточно перевернуть на 180°.
Физика движения «глазков»
Матрица не просто показывает результат. Шесть «глазков» кубика движутся по экрану: отскакивают от краёв и плавно теряют скорость. Интенсивность встряхивания (число срабатываний датчиков за секунду) задаёт начальную «скорость» глазков. При остановке встряхивания глазки катятся от случайных позиций к правильным для выпавшего значения.
Питание
Изначально использовались два элемента CR2032 (3 В), но их напряжение садилось через несколько минут. Перешли на две ААА-батарейки (3 В) с повышающим преобразователем MT3608 до 5 В на пин VIN Arduino.
Корпус: 3D-печать
Корпус из трёх частей (верх, низ с держателем батареек, матовой плексигласовый экран) спроектирован в Fusion 360. Матовый плексиглас перед матрицей скрывает негорящие светодиоды и делает картинку контрастнее. Детали соединяются прессовкой (зазор 0,1 мм) — без клея, но надёжно держатся и разбираются для смены батареек.
Сборка электроники
Arduino Nano установлен на гнездовый колодек (какшилд). LED-матрица подключена к пинам 5 (DIN), 6 (CS), 7 (CLK). Питание матрицы — через перемычки на 5V и GND. Два наклонных датчика подключены к пинам 2 и 3 (с подтяжкой к 5V и GND).
Необходимые компоненты (чек-лист)
- Arduino Nano (или ESP8266)
- 2 × наклонных датчика (с встроенными подтягивающими резисторами)
- LED-матрица 8×8 с MAX7219
- Гнездовый колодек
- Вкл/выкл тумблер
- Монтажная плата
- 2 × батарейки ААА
Программный код (wuerfel8x8.ino)
/* Blog-Artikel: Schüttel-Würfel mit LED Matrix und Bewegungssimulation
https://techpluscode.de/schuettel-wuerfel-mit-led-matrix-und-bewegungssimulation/
techpluscode.de Copyright 2019 von Thomas Angielsky */
#include "LedControl.h"
int PinTiltX = 2;
int PinTiltY = 3;
int PinCLK = 7;
int PinCS = 6;
int PinDIN = 5;
LedControl lc = LedControl(PinDIN, PinCLK, PinCS, 1);
// Координаты точек кубика на матрице [значение][точка][x/y]
int DicePic[8][6][2] = {
{ {9,9}, {9,8}, {9,7}, {9,6}, {9,5}, {9,4} }, // 0: пусто/старт
{ {4,4}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} }, // 1
{ {2,2}, {6,6}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} }, // 2
{ {2,6}, {6,2}, {4,4}, {-1,-1}, {-1,-1}, {-1,-1} }, // 3
{ {2,2}, {2,6}, {6,2}, {6,6}, {-1,-1}, {-1,-1} }, // 4
{ {2,2}, {2,6}, {6,2}, {6,6}, {4,4}, {-1,-1} }, // 5
{ {2,1}, {2,4}, {2,7}, {6,1}, {6,4}, {6,7} }, // 6
{ {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} } // 7: старт
};
float DiceXpos[6], DiceYpos[6];
float DiceXdir[6], DiceYdir[6];
volatile byte DiceXspeed[6], DiceYspeed[6];
int DiceValue;
unsigned long timestamp;
byte Mode;
volatile int shakes;
int ShakesPerSecond;
int step;
void InterruptChecks() { shakes++; timestamp = millis(); }
void SetSpeedX() { if (Mode==0) for(int i=0;i<6;i++) if(DiceXspeed[i]<255) DiceXspeed[i]+=5; InterruptChecks(); }
void SetSpeedY() { if (Mode==0) for(int i=0;i<6;i++) if(DiceYspeed[i]<255) DiceYspeed[i]+=5; InterruptChecks(); }
void ShowLed(int x, int y, bool on) { if(x<8 && y<8 && x>=0 && y>=0) lc.setLed(0,x,y,on); }
void ShowDot(int x, int y, bool on) {
ShowLed(x-1,y-1,on); ShowLed(x,y-1,on); ShowLed(x-1,y,on); ShowLed(x,y,on);
}
void ShowDicePic(int value) {
boolean done;
for(int i=0;i<6;i++) { DiceXspeed[i]=100; DiceYspeed[i]=100;
DiceXdir[i]=0; if(int(DiceXpos[i])>DicePic[value][i][0]) DiceXdir[i]=-1; else if(int(DiceXpos[i])DicePic[value][i][1]) DiceYdir[i]=-1; else if(int(DiceYpos[i])7){pos=7; dir*=-1;} if(pos<1){pos=1; dir*=-1;} }
if(sp>0) sp--;
}
void MoveDots() {
for(int i=0;i<6;i++) { DoStep(DiceXpos[i],DiceXdir[i],DiceXspeed[i],true); DoStep(DiceYpos[i],DiceYdir[i],DiceYspeed[i],true); }
lc.clearDisplay(0);
for(int i=0;i<6;i++) ShowDot(int(DiceXpos[i]),int(DiceYpos[i]),true);
}
void setup() {
lc.shutdown(0,false); lc.setIntensity(0,8); lc.clearDisplay(0);
randomSeed(analogRead(0)); DiceValue=0;
for(int i=0;i<6;i++) {
DiceXpos[i]=DicePic[7][i][0]; DiceYpos[i]=DicePic[7][i][1];
DiceXdir[i]=random(3)-1; DiceYdir[i]=random(3)-1;
DiceXspeed[i]=random(126)+120; DiceYspeed[i]=random(126)+120;
}
pinMode(PinTiltX, INPUT_PULLUP); pinMode(PinTiltY, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PinTiltX), SetSpeedX, CHANGE);
attachInterrupt(digitalPinToInterrupt(PinTiltY), SetSpeedY, CHANGE);
lc.clearDisplay(0); timestamp=millis(); Mode=1;
ShowDicePic(6); delay(1000); lc.clearDisplay(0); Mode=0;
Serial.begin(9600); step=0; shakes=0;
}
void loop() {
delay(50); step++;
if(step>20) { step=0; ShakesPerSecond=shakes; shakes=0; }
if(Mode==0) {
MoveDots();
if(millis()-timestamp>2000) { Mode=1; DiceValue=random(6)+1; ShowDicePic(DiceValue); }
}
if(ShakesPerSecond>5) Mode=0;
}
Схемы и 3D-модели
Принципиальная схема и файлы для 3D-печати корпуса доступны в разделе проекта.
Производственный процесс
- История, дизайн и производство кубиков: от древних инструментов к современным играм
- Панель переключателей и LED для Microsoft Flight Simulator 2020 на Arduino
- Цифровые кости на Arduino: генератор случайных чисел с LCD-дисплеем и кнопкой
- Сборка Bluetooth-робота-шпиона на Arduino с управлением с телефона
- FlickMote: жестовое IR-пульт для умного дома на Arduino 101 и ультразвуковом датчике
- DIY Arduino TV-B-Gone: универсальный пульт для выключения любого телевизора
- Создание пользовательских LED-часов с будильником — видно с 12 метров
- Tetris на Arduino Nano: самостоятельная сборка 16×8 LED-матрицы на 74HC595
- Find Me: Умный поисковик вещей на Arduino и Bluetooth
- Оптимизированные решения питания для проектов на Arduino