Электронный игральный кубик

Проекты на Arduino и Slot Shield

Чем ближе друзья живут, тем их сложнее собрать. В ход идёт куча предлогов — передвинуть мебель на даче, починить машину в гаражно-оздоровительном комплексе. Порубиться в тихие настольные игры — «Манчкин» или ещё какую-нибудь «Цивилизацию». И вот, момент настал, пенные напитки в холодильнике, все собрались и вдруг выясняется, кто-то потерял игральные кубики. Если кубики обычные, шестигранные, их можно поискать у младшего брата. Но как быть с четырёх-, двенадцати- или двадцатигранными? Подземелья так и останутся неисследованными, а драконы — непобеждёнными.

Наши универсальные электронные кости работают в режиме эмуляции стандартных кубиков d4, d6, d12 и d20. Режим работы задаётся потенциометром и выводятся на левую LED-матрицу. Чтобы запустить генератор случайных чисел, возьмите гаджет в руки и встряхните его. Гироскоп запустит генератор случайных чисел и результат «броска» появится на правой матрице. Для четырёх- и шестигранных кубиков значение выводится крупными точками, как на настоящих костях, для остальных кубиков используется цифровая индикация.

Что потребуется

Полный сет компонентов проекта. В сет входят:

Видеоинструкция

Как собрать

Установите Troyka Slot Shield на Iskra Neo

Вставьте две LED матрицы 8×8 в левый и правый слоты верхнего ряда. Чтобы у матриц были разные адреса, капните припой на любую контактную площадку правого модуля. Как это сделать — смотрите на странице описания матриц.

Установите гироскоп в оставшийся свободным слот верхнего ряда.

Переверните потенциометр вверх ногами и вставьте в центральный слот нижнего ряда.

Скетч

Прошейте контроллер скетчем через Arduino IDE.

dice.ino
// библиотека для работы I²C
#include "Wire.h"
// библиотека для работы со светодиодной матрицей
#include "TroykaLedMatrix.h"
// библиотека для работы с модулями IMU
#include <TroykaIMU.h>
 
// создаём объекты matrix для работы с матрицами
// для каждой матрицы передаём свой адрес
// подробнее читайте на:
// http://wiki.amperka.ru/продукты:troyka-led-matrix
TroykaLedMatrix matrix1;
TroykaLedMatrix matrix2(0x61);
// создаём объект для работы с гироскопом
Gyroscope gyro;
 
// порог скорости вращения гироскопа
#define ROTATE_SPEED_TRESHOLD 500
 
enum {FOUR = 4, SIX = 6, TWELWE = 12, TWENTY = 20};
 
// массив закраски всей матрицы
const uint8_t allPixels[] {
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B11111111
};
 
// массив картнки цифры 4
const uint8_t four[] {
  B00000000,
  B00001000,
  B00011000,
  B00101000,
  B00111100,
  B00001000,
  B00001000,
  B00000000
};
 
// массив картнки цифры 6
const uint8_t six[] {
  B00000000,
  B00011000,
  B00100000,
  B00111000,
  B00100100,
  B00100100,
  B00011000,
  B00000000,
};
 
// массив картнки цифры 12
const uint8_t twelwe[] {
  B00000000,
  B00100100,
  B01101010,
  B00100010,
  B00100100,
  B00101000,
  B00101110,
  B00000000
};
 
// массив картнки цифры 20
const uint8_t twenty[] {
  B00000000,
  B01000110,
  B10101001,
  B00101001,
  B01001001,
  B10001001,
  B11100110,
  B00000000
};
 
// массив значений кубика для режима 4d
const uint8_t face4D[][8] {
  { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00},
  { 0x00, 0x06, 0x06, 0x00, 0x00, 0x60, 0x60, 0x00},
  { 0x03, 0x03, 0x00, 0x18, 0x18, 0x00, 0xC0, 0xC0},
  { 0x18, 0x18, 0x00, 0xC3, 0xC3, 0x00, 0x18, 0x18}
};
 
// массив значений кубика для режима 6d
const uint8_t face6D[][8] {
  { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00},
  { 0x00, 0x06, 0x06, 0x00, 0x00, 0x60, 0x60, 0x00},
  { 0x03, 0x03, 0x00, 0x18, 0x18, 0x00, 0xC0, 0xC0},
  { 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00},
  { 0xC3, 0xC3, 0x00, 0x18, 0x18, 0x00, 0xC3, 0xC3},
  { 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66}
};
 
// массив значений кубика для режима 12d и 20d
const uint8_t face20D[][8] {
  { 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x00},  // 1
  { 0x00, 0x18, 0x24, 0x04, 0x08, 0x10, 0x3C, 0x00},  // 2
  { 0x00, 0x38, 0x08, 0x10, 0x08, 0x08, 0x30, 0x00},  // 3
  { 0x00, 0x08, 0x18, 0x28, 0x3C, 0x08, 0x08, 0x00},  // 4
  { 0x00, 0x3C, 0x20, 0x38, 0x04, 0x24, 0x18, 0x00},  // 5
  { 0x00, 0x18, 0x20, 0x38, 0x24, 0x24, 0x18, 0x00},  // 6
  { 0x00, 0x3C, 0x04, 0x04, 0x08, 0x10, 0x10, 0x00},  // 7
  { 0x00, 0x18, 0x24, 0x18, 0x24, 0x24, 0x18, 0x00},  // 8
  { 0x00, 0x18, 0x24, 0x24, 0x1C, 0x04, 0x18, 0x00},  // 9
  { 0x00, 0x26, 0x69, 0x29, 0x29, 0x29, 0x26, 0x00},  // 10
  { 0x00, 0x22, 0x66, 0x22, 0x22, 0x22, 0x22, 0x00},  // 11
  { 0x00, 0x24, 0x6A, 0x22, 0x24, 0x28, 0x2E, 0x00},  // 12
  { 0x00, 0x2E, 0x62, 0x24, 0x22, 0x22, 0x2C, 0x00},  // 13
  { 0x00, 0x22, 0x66, 0x2A, 0x2F, 0x22, 0x22, 0x00},  // 14
  { 0x00, 0x2F, 0x68, 0x2E, 0x21, 0x29, 0x26, 0x00},  // 15
  { 0x00, 0x26, 0x68, 0x2E, 0x29, 0x29, 0x26, 0x00},  // 16
  { 0x00, 0x2F, 0x61, 0x21, 0x22, 0x24, 0x24, 0x00},  // 17
  { 0x00, 0x26, 0x69, 0x26, 0x29, 0x29, 0x26, 0x00},  // 18
  { 0x00, 0x26, 0x69, 0x29, 0x27, 0x21, 0x26, 0x00},  // 19
  { 0x00, 0x46, 0xA9, 0x29, 0x49, 0x89, 0xE6, 0x00}   // 20
};
 
void setup()
{
  // открываем последовательный порт
  Serial.begin(115200);
  // выводим сообщение о начале инициализации
  Serial.println("Gyroscope init...");
  // инициализация гироскопа
  gyro.begin();
  // устанавливаем чувствительность гироскопа
  // 250dps — по умолчанию, 500dps, 2000dps
  gyro.setRange(RANGE_250DPS);
  // выводим сообщение об удачной инициализации
  Serial.println("Initialization completed");
  // начало работы с матрицами
  matrix1.begin();
  matrix2.begin();
  // очищаем матрицы
  matrix1.clear();
  matrix2.clear();
}
 
void loop()
{
  // считываем количесвто граней
  int countFace = readCountFace();
  // выводим количество граней на первую матрицу
  switch(countFace) {
    case FOUR:
      matrix1.drawBitmap(four);
      break;
    case SIX:
      matrix1.drawBitmap(six);
      break;
    case TWELWE:
      matrix1.drawBitmap(twelwe);
      break;
    case TWENTY:
      matrix1.drawBitmap(twenty);
      break;
  }
  // считываем абсолютную скорость вращения гироскопа по трём осям
  float cubeRotate = abs(gyro.readDegPerSecX())
  + abs(gyro.readDegPerSecY())
  + abs(gyro.readDegPerSecZ());
  // если скорость вращения гироскопа превысила порог
  if (cubeRotate > ROTATE_SPEED_TRESHOLD) {
    // считаем это за бросок кубика
    // бросаем кубик и отображаем результат
    dropCubeAndPrintResult(countFace);
    delay(1000);
  }
}
 
// функция считывания количество граней
int readCountFace() {
  // считываем показания с потенциометра
  int sensorValue = analogRead(A1);
  if (sensorValue <= 255) {
    return TWENTY;
  } else if (sensorValue > 255 && sensorValue <= 512) {
    return TWELWE;
  } else if (sensorValue > 512 && sensorValue <= 767) {
    return SIX;
  } else if (sensorValue > 767 && sensorValue <= 1023) {
    return FOUR;
  }
}
 
// функция броска кубика и отображение результата
void dropCubeAndPrintResult(int countFace) {
  int number = random(1, countFace + 1);
  randomPixels();
  Serial.println(number);
  switch (countFace) {
    case FOUR:
      matrix2.drawBitmap(face4D[number - 1]);
      break;
    case SIX:
      matrix2.drawBitmap(face6D[number - 1]);
      break;
    case TWELWE:
      matrix2.drawBitmap(face20D[number - 1]);
      break;
    case TWENTY:
      matrix2.drawBitmap(face20D[number - 1]);
      break;
  }
}
 
void randomPixels() {
  matrix2.clear();
  for (int i = 0; i < 20; i++) {
    // заполняем матрицу случайным пикселем
    matrix2.drawPixel(random(8),random(8));
    // очищаем на матрице случайный пиксель
    matrix2.clearPixel(random(8),random(8));
    // ждём 50 мс
    delay(30);
  }
  matrix2.clear();
}

Что дальше?

Хотите собрать другой девайс? Выберите своё будущее устройство из списка проектов на Slot Shield.

Если вы захотите изменить графику, рекомендуем воспользоваться удобным редактором для LED матриц.

Часто задаваемые вопросы