


Want to create the same ?
8BitConsole
- DIY electronic kit with all parts needed to create gaming device powered by ATmega328P chip (the same chip support Arduino Uno)
The ATmega328 is a single-chip microcontroller created by Atmel.
It has 128 LEDs as a main display and 3 digits additional display.
The console has a battery holder for three AAA batteries as the main power supply.
Optionally, you can power the device from the USB port.
ATmega328 is delivered with Arduino bootloader and first game
Important. This is a kit for self-assembly
#001 - 8BitConsole
- PCB x 1
- Tact Switch 12x12mm (white or yellow) x 4
- Tact Switch 12x12mm (red) x 1
- Tact Switch 12x12mm (black) x 1
- Resistor 10kΩ x 10
- Resistor 100Ω x 1
- Capacitor 10uF x 5
- Capacitor 22pF x 2
- ATmega328P x 1
- MAX7219 x 3
- 16mhz crystal x 1
- Led Matrix 8x8 red x 2
- 8-segment display x3 - 14mm red - common anode x 1
- USB mini type B socket x 1
- Active buzzer with generator 5V 12mm x 1
- Slide switch 2-position x 1
- Battery storage case for 3 packs AAA x 1
- Screws and nuts (for battery holder) x 1
- Goldpin x 6 x 1
- Plexi case - top x 1
- Plexi case - bottom x 1
- Screw and nut (for battery holder) x 2
- Distance bolt x 8
- Screw x 8
Code
#include "LedControl.h"
const uint64_t LOADER[] = {
0x000000000f0f0f0f,
0x00000000f0f0f0f0,
0xf0f0f0f000000000,
0x0f0f0f0f00000000,
0x0f0f0f0ff0f0f0f0,
0xf0f0f0f00f0f0f0f,
0xffffffffffffffff,
0x0000000000000000,
};
const uint64_t IMG_LEFT = 0x1018fcfefc181000;
const uint64_t IMG_RIGHT = 0x10307efe7e301000;
const uint64_t IMG_UP = 0x383838fe7c381000;
const uint64_t IMG_DOWN = 0x10387cfe38383800;
const uint64_t IMG_A = 0x6666667e66663c00;
const uint64_t IMG_B = 0x3e66663e66663e00;
const uint64_t IMG_EMPTY = 0x0000000000000000;
const uint64_t FULL = 0xffffffffffffffff;
const int c = 261;
const int d = 294;
const int e = 329;
const int f = 349;
const int g = 391;
const int gS = 415;
#define BTN_RIGHT A0
#define BTN_UP A1
#define BTN_DOWN A2
#define BTN_LEFT A3
#define BTN_A A4
#define BTN_B A5
#define LOAD_PIN 7
#define CLOCK_PIN 6
#define DATA_PIN 8
#define CLOCK_COUNTER_PIN 2
#define LOAD_COUNTER_PIN 3
#define DATA_COUNTER_PIN 4
#define BUZZER_PIN 5
LedControl display = LedControl(DATA_PIN, CLOCK_PIN, LOAD_PIN);
LedControl display_counter = LedControl(DATA_COUNTER_PIN, CLOCK_COUNTER_PIN, LOAD_COUNTER_PIN,3);
void setup() {
display.clearDisplay(0);
display.shutdown(0, false);
display.setIntensity(0, 5);
display_counter.shutdown(0,false);
display_counter.setIntensity(0,9);
display_counter.clearDisplay(0);
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(A4, INPUT);
pinMode(A5, INPUT);
showLoader();
displayImage(IMG_EMPTY);
counter_demo();
full();
}
void full(){
displayImage(FULL);
print(1, 8);
print(2, 8);
print(3, 8);
delay(1000);
}
void loop() {
if(digitalRead(BTN_RIGHT) == 1) {
beep(c, 255);
print(1, 1);
print(2, 1);
print(3, 1);
displayImage(IMG_RIGHT);
}
if(digitalRead(BTN_LEFT) == 1) {
beep(d, 255);
print(1, 2);
print(2, 2);
print(3, 2);
displayImage(IMG_LEFT);
}
if(digitalRead(BTN_UP) == 1) {
beep(e, 255);
print(1, 3);
print(2, 3);
print(3, 3);
displayImage(IMG_UP);
}
if(digitalRead(BTN_DOWN) == 1) {
beep(f, 255);
print(1, 4);
print(2, 4);
print(3, 4);
displayImage(IMG_DOWN);
}
if(digitalRead(BTN_A) == 1) {
beep(g, 255);
print(1, 5);
print(2, 5);
print(3, 5);
displayImage(IMG_A);
}
if(digitalRead(BTN_B) == 1) {
beep(gS, 255);
print(1, 6);
print(2, 6);
print(3, 6);
displayImage(IMG_B);
}
}
void counter_demo() {
int first;
int second;
int third;
for(int base=0;base<1000;base++) {
first = base / 100 % 10;
second = base / 10 % 10;
third = base % 10;
print(1, first);
print(2, second);
print(3, third);
delay(10);
}
}
void showLoader() {
for (int i = 0; i <= 7; i++) {
displayImage(LOADER[i]);
delay(300);
}
}
void beep(int note, int duration) {
tone(BUZZER_PIN, note, duration);
delay(200);
noTone(BUZZER_PIN);
}
void print(byte d,byte n) {
display_counter.setColumn(0,d,pgm_read_byte_near(charTable + n));
}
void displayImage(uint64_t image) {
for (int i = 0; i < 8; i++) {
byte row = (image >> i * 8) & 0xFF;
for (int j = 0; j < 8; j++) {
display.setLed(0, i, j, bitRead(row, j));
}
}
}
8BitConsole
#include "Arduino.h"
#include "LEDMatrix.h"
#include "Button.h"
#include "Buzzer.cpp"
#include "Config.h"
#include "CounterDisplay.h"
#include "Xetris.cpp"
LEDMatrix ledMatrix(2, CLOCK_PIN, DATA_PIN, LOAD_PIN);
CounterDisplay counterDisplay(DATA_COUNTER_PIN, CLOCK_COUNTER_PIN, LOAD_COUNTER_PIN, 3);
Button rightButton(RIGHT_BUTTON_PIN);
Button leftButton(LEFT_BUTTON_PIN);
Button actionAButton(A_BUTTON_PIN);
Button actionBButton(B_BUTTON_PIN);
Button downButton(DOWN_BUTTON_PIN);
Buzzer buzzer(BUZZER_PIN);
Xetris xetris(ledMatrix, counterDisplay, rightButton, leftButton, actionAButton, actionBButton, downButton);
void setup() {
xetris.setup();
}
void loop() {
xetris.loop();
}
Config.h
#define LOAD_PIN 7
#define CLOCK_PIN 6
#define DATA_PIN 8
#define CLOCK_COUNTER_PIN 2
#define LOAD_COUNTER_PIN 3
#define DATA_COUNTER_PIN 4
#define RIGHT_BUTTON_PIN A0
#define UP_BUTTON_PIN A1
#define DOWN_BUTTON_PIN A2
#define LEFT_BUTTON_PIN A3
#define A_BUTTON_PIN A4
#define B_BUTTON_PIN A5
#define BUZZER_PIN 5
Button.h
#ifndef BUTTON_H
#define BUTTON_H
class Button {
public:
Button(int);
bool pressed();
private:
int _pin;
int _lastState;
};
#endif
Button.cpp
#include "Arduino.h"
#include "Button.h"
Button::Button(int pin) {
_pin = pin;
_lastState = LOW;
pinMode(_pin, INPUT);
}
bool Button::pressed() {
int state = digitalRead(_pin);
if (state != _lastState) {
_lastState = state;
return state == HIGH;
}
return false;
}
Buzzer.cpp
#include "Arduino.h"
class Buzzer {
public:
Buzzer(int pin) {
_pin = pin;
pinMode(_pin, OUTPUT);
}
void beep(int note, int duration) {
tone(_pin, note);
delay(duration);
noTone(_pin);
}
private:
int _pin;
const int _c = 261;
const int _d = 294;
const int _e = 329;
const int _f = 349;
const int _g = 391;
const int _gS = 415;
};
CounterDisplay.cpp
#include "Arduino.h"
#include "LedControl.h"
#include "CounterDisplay.h"
CounterDisplay::CounterDisplay(int dataPin, int clockPin, int loadPin, int digits) {
_ledControl = new LedControl(dataPin, clockPin, loadPin, digits);
_ledControl -> shutdown(0, false);
_ledControl -> setIntensity(0, 2);
_ledControl -> clearDisplay(0);
}
void CounterDisplay::show(int base) {
if (base > 99) print_digit(1, base / 100 % 10);
if (base > 9) print_digit(2, base / 10 % 10);
print_digit(3, base % 10);
}
void CounterDisplay::print_digit(byte d, byte n) {
_ledControl -> setColumn(0, d, pgm_read_byte_near(charTable + n));
}
CounterDisplay.h
#ifndef CounterDisplay_H
#define CounterDisplay_H
#include "LedControl.h"
class CounterDisplay {
public:
CounterDisplay(int, int, int, int);
void show(int);
private:
LedControl* _ledControl;
void print_digit(byte, byte);
};
#endif
LEDMatrix.cpp
#include "Arduino.h"
#include "LEDMatrix.h"
LEDMatrix::LEDMatrix(int numberOfMatrices, int clockPin, int dataPin, int loadPin) {
_numberOfMatrices = numberOfMatrices;
_clockPin = clockPin;
_dataPin = dataPin;
_loadPin = loadPin;
pinMode(_dataPin, OUTPUT);
pinMode(_clockPin, OUTPUT);
pinMode(_loadPin, OUTPUT);
//initiation of the max 7219
WriteAll(max7219_reg_scanLimit, 0x07);
WriteAll(max7219_reg_decodeMode, 0x00); // using an led matrix (not digits)
WriteAll(max7219_reg_shutdown, 0x01); // not in shutdown mode
WriteAll(max7219_reg_displayTest, 0x00); // no display test
for (int e = 1; e <= 8; e++) { // empty registers, turn all LEDs off
WriteAll(e, 0);
}
// the first 0x0f is the value you can set
// range: 0x00 to 0x0f
WriteAll(max7219_reg_intensity, 0x01 & 0x0f);
}
LEDMatrix::WriteAll(byte reg, byte col) {
digitalWrite(_loadPin, LOW);
for (int c = 1; c <= _numberOfMatrices; c++) {
putByte(reg);
putByte(col); //((data & 0x01) * 256) + data >> 1); // put data
}
digitalWrite(_loadPin, LOW);
digitalWrite(_loadPin, HIGH);
}
LEDMatrix::WriteOne(byte matrixIndex, byte reg, byte col) {
digitalWrite(_loadPin, LOW); // begin
for (int c = 2; c > matrixIndex; c--) {
putByte(0); // means no operation
putByte(0); // means no operation
}
putByte(reg); // specify register
putByte(col); //((data & 0x01) * 256) + data >> 1); // put data
for (int c = matrixIndex - 1; c >= 1; c--) {
putByte(0); // means no operation
putByte(0); // means no operation
}
digitalWrite(_loadPin, LOW); // and load the stuff
digitalWrite(_loadPin, HIGH);
}
void LEDMatrix::putByte(byte data) {
byte i = 8;
byte mask;
while (i > 0) {
mask = 0x01 << (i - 1); // get bitmask
digitalWrite(_clockPin, LOW); // tick
if (data & mask) { // choose bit
digitalWrite(_dataPin, HIGH); // send 1
} else {
digitalWrite(_dataPin, LOW); // send 0
}
digitalWrite(_clockPin, HIGH); // tock
--i; // move to lesser bit
}
}
LEDMatrix.h
#ifndef LEDMATRIX_H
#define LEDMATRIX_H
class LEDMatrix {
public:
LEDMatrix(int numberOfMatrices, int clockPin, int dataPin, int loadPin);
WriteAll(byte reg, byte col);
WriteOne(byte matrixIndex, byte reg, byte col);
private:
byte max7219_reg_noop = 0x00;
byte max7219_reg_digit0 = 0x01;
byte max7219_reg_digit1 = 0x02;
byte max7219_reg_digit2 = 0x03;
byte max7219_reg_digit3 = 0x04;
byte max7219_reg_digit4 = 0x05;
byte max7219_reg_digit5 = 0x06;
byte max7219_reg_digit6 = 0x07;
byte max7219_reg_digit7 = 0x08;
byte max7219_reg_decodeMode = 0x09;
byte max7219_reg_intensity = 0x0a;
byte max7219_reg_scanLimit = 0x0b;
byte max7219_reg_shutdown = 0x0c;
byte max7219_reg_displayTest = 0x0f;
int _numberOfMatrices;
int _clockPin;
int _dataPin;
int _loadPin;
void putByte(byte data);
};
#endif
Xetris.cpp
#include "Arduino.h"
#include "LEDMatrix.h"
#include "CounterDisplay.h"
#include "Button.h"
class Xetris {
public:
int score = 0;
long gameInterval = 300;
int currentShapeIndex = 0;
int currentRotation = 0;
byte currentMatrix[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
word currentShape;
int currentX = 0;
int currentY = 0;
int previousRotateButtonState = LOW;
int previousSlamButtonState = LOW;
word shapes[7][4] = {
{0xCC00, 0xCC00, 0xCC00, 0xCC00}, // O
{0x4444, 0x0F00, 0x2222, 0x0F00}, // I
{0x4E00, 0x4640, 0x0E40, 0x4C40}, // T
{0x4460, 0x0E80, 0xC440, 0x2E00}, // L
{0x44C0, 0x8E00, 0x6440, 0x0E20}, // J
{0x4C80, 0xC600, 0x4C80, 0xC600}, // Z
{0x8C40, 0x6C00, 0x8C40, 0x6C00} // S
};
Xetris(LEDMatrix& m,
CounterDisplay& cd,
Button& rightBtn,
Button& leftBtn,
Button& actionABtn,
Button& actionBBtn,
Button& downBtn
) {
ledMatrix = &m;
counterDisplay = &cd;
rightButton = &rightBtn;
leftButton = &leftBtn;
actionAButton = &actionABtn;
actionBButton = &actionBBtn;
downButton = &downBtn;
}
void setup() {
randomSeed(analogRead(6));
setupNewGame();
playDeathAnimation();
spawnNewShape();
Serial.begin(9600);
}
void loop() {
byte matrix[16];
renderShapeInMatrix(currentMatrix, matrix, currentShape, currentX, currentY);
renderMatrix(matrix);
if (leftButton->pressed()) {
moveLeftIfPossible();
}
if (rightButton->pressed()) {
moveRightIfPossible();
}
if (actionAButton->pressed()) {
int nextRotation = currentRotation + 1;
if (nextRotation > 3) {
nextRotation = 0;
}
word nextShape = shapes[currentShapeIndex][nextRotation];
if (!collides(currentMatrix, nextShape, currentX, currentY)) {
currentRotation = nextRotation;
currentShape = nextShape;
}
}
if (downButton->pressed()) {
int bottomOffset = bottomOffsetPositionForShape(currentShape);
while (!collides(currentMatrix, currentShape, currentX, currentY + 1) && currentY + 4 - bottomOffset <= 15) {
currentY++;
}
currentY--;
}
updateBlockPosition();
}
void setupNewGame() {
currentShapeIndex = 0;
currentX = 0;
currentY = 0;
currentRotation = 0;
score = 0;
gameInterval = 300;
for (int i = 0; i < 16; i++) {
currentMatrix[i] = 0x0;
}
counterDisplay->show(score);
spawnNewShape();
}
void spawnNewShape() {
currentShapeIndex = random(7);
currentShape = shapes[currentShapeIndex][currentRotation];
currentY = -4;
currentX = 3;
}
void playDeathAnimation() {
for (int i = 0; i < 16; i++) {
if (i < 8) {
ledMatrix -> WriteOne(1, i + 1, 0xff);
} else {
ledMatrix -> WriteOne(2, i + 1 - 8, 0xff);
}
delay(75);
}
for (int i = 0; i < 16; i++) {
if (i < 8) {
ledMatrix->WriteOne(1, i + 1, 0x00);
} else {
ledMatrix->WriteOne(2, i + 1 - 8, 0x00);
}
delay(75);
}
}
void updateBlockPosition() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= gameInterval) {
previousMillis = currentMillis;
int bottomOffset = bottomOffsetPositionForShape(currentShape);
if (collides(currentMatrix, currentShape, currentX, currentY + 1) || currentY + 4 - bottomOffset > 15) {
// Check for game over
if (currentY < 0) {
playDeathAnimation();
setupNewGame();
return;
}
renderShapeInMatrix(currentMatrix, currentMatrix, currentShape, currentX, currentY);
spawnNewShape();
int linesRemoved = removeFullLines(currentMatrix);
score += linesRemoved;
// Increase the game speed
gameInterval -= linesRemoved * 3;
show(score);
} else {
currentY++;
}
}
}
void renderMatrix(byte *matrix) {
for (int i = 0; i < 16; i++) {
if (i < 8) {
ledMatrix->WriteOne(1, i + 1, matrix[i]);
} else {
ledMatrix->WriteOne(2, i + 1 - 8, matrix[i]);
}
}
}
int removeFullLines(byte *matrix) {
int res = 0;
for (int i = 0; i < 16; i++) {
if (matrix[i] == 0xff) { // Full line
matrix[i] = 0x0;
shiftLinesDown(matrix, i);
res++;
}
}
return res;
}
void shiftLinesDown(byte *matrix, int toIndex) {
matrix[0] = 0x0;
for (int i = toIndex; i > 0; i--) {
matrix[i] = matrix[i - 1];
}
}
void renderShapeInMatrix(byte *original, byte *destination, word shape, int x, int y) {
for (int i = 0; i < 16; i++) {
destination[i] = original[i];
}
for (int i = 0; i < 4; i++) {
if (y + i < 0) { // Allow for negative Y (to render half a shape)
continue;
}
int shift = (3 - i) * 4;
byte rowsValue = (shape & (0xf << shift)) >> shift;
// Move to left hand side
int lOffset = leftOffsetPositionForShape(shape);
rowsValue = rowsValue << (4 + lOffset);
// Move to right according to x and offset
rowsValue = rowsValue >> (x + lOffset);
destination[y + i] = original[y + i] | rowsValue;
}
}
// Find leftmost active bit in shape
int leftOffsetPositionForShape(word shape) {
int offset = 4;
for (int i = 0; i < 4; i++) {
int shift = (3 - i) * 4;
byte rowsValue = (shape & (0xf << shift)) >> shift;
byte test = 0b1000;
for (int m = 0; m < 3; m++) {
int l = rowsValue & test;
if (l > 0 && m < offset) {
offset = m;
}
test >>= 1;
}
}
return offset;
}
bool collides(byte *matrix, word shape, int x, int y) {
for (int i = 0; i < 4; i++) {
if (y + i < 0) {
continue;
}
int shift = (3 - i) * 4;
byte rowsValue = (shape & (0xf << shift)) >> shift;
// Move to left hand side
int lOffset = leftOffsetPositionForShape(shape);
rowsValue = rowsValue << (4 + lOffset);
// Move to right according to x and offset
rowsValue = rowsValue >> (x + lOffset);
if ((matrix[y + i] & rowsValue) > 0) {
return true;
}
}
return false;
}
int rightOffsetPositionForShape(word shape) {
int offset = 4;
for (int i = 0; i < 4; i++) {
int shift = (3 - i) * 4;
byte rowsValue = (shape & (0xf << shift)) >> shift;
byte test = 1;
for (int m = 0; m < 3; m++) {
int l = rowsValue & test;
if (l > 0 && m < offset) {
offset = m;
}
test <<= 1;
}
}
return offset;
}
// Find offset for bottom active bit in shape
int bottomOffsetPositionForShape(word shape) {
for (int i = 3; i >= 0; i--) {
int shift = (3 - i) * 4;
byte rowsValue = (shape & (0xf << shift)) >> shift;
if (rowsValue > 0) {
return 3 - i;
}
}
}
void moveRightIfPossible() {
// Check that currentX + bitIndex + 1 < board_width (8)
int offset = rightOffsetPositionForShape(currentShape);
bool wouldCollide = collides(currentMatrix, currentShape, currentX + 1, currentY);
if (currentX + (4 - offset) < 8 && !wouldCollide) {
currentX++;
}
}
void moveLeftIfPossible() {
// Check that currentX + bitIndex - 1 >= 0
int offset = leftOffsetPositionForShape(currentShape);
bool wouldCollide = collides(currentMatrix, currentShape, currentX - 1, currentY);
if (currentX + offset - 1 >= 0 && !wouldCollide) {
currentX--;
}
}
void show(int n) {
counterDisplay->show(n);
}
private:
unsigned long previousMillis = 0;
LEDMatrix* ledMatrix;
CounterDisplay* counterDisplay;
Button* rightButton;
Button* leftButton;
Button* actionAButton;
Button* actionBButton;
Button* downButton;
};