ATtiny85 Mini Arcade: Snake – Bouw een Handheld OLED Game Console
Onderdelen en Benodigdheden
| Microchip ATtiny85 | 1 |
| Grafische OLED, 128x64 | 1 |
| SparkFun microB USB Breakout | 1 |
| Arduino Nano R3 (voor programmeren ATtiny85) | 1 |
Benodigde Tools en Machines
- Solderbout
- 3D-printer
Apps en Online Services
- Autodesk Fusion 360
- Arduino IDE
Over dit Project
Inspiratie en Vorig Project
In december 2017 bouwde ik een Handheld Arduino Pong Console met Arduino Nano, OLED-scherm en twee knoppen.
Die was wel wat groot en onhandig. Recentelijk wilde ik oude projecten hercreëren, maar nu een heel kleine console voor Snake.
De Gekozen Componenten
Voor een kleine console kon ik geen Arduino-devboard gebruiken, dus koos ik de ATtiny85.
De ATtiny85 heeft voldoende I/O: twee ADC-pinnen, een I2C-poort en een GPIO-pin. Ik koos een eenvoudige 2-as joystick/schakelaar module – makkelijk te interfacen, alleen 3 signaalpinnen nodig.
Voor het display: DFRobot's 128x64 OLED-scherm – klein formaat, voldoende resolutie.
Solderen van het Systeem
Eerst soldeerde ik een ATtiny85 op een SOP-8 naar DIP-8 breakout PCB en vervolgens op een klein perforatieprintplaatje. Daarna twee headers: één voor programmeren, één voor het scherm. Vervolgens de analoge joystick-pinnen aansluiten en bedraden. Tot slot een micro-USB breakout bord voor voeding.
Behuizing Ontwerpen
De behuizing is ontworpen in Fusion 360. Ik begon met het maken en positioneren van de gebruikte componenten, daarna de behuizing eromheen.
Ik wilde een arcade-kastje uit de jaren '80 nabootsen, maar heel klein. Hieronder enkele renders:
De Game Programmeren
Snake is eenvoudig te programmeren. Maximale slangenlengte is 30 (RAM besparen): na 29 pixels eten wint de speler. Voor segmentbijhouder: een 2D-array met geordende paren per segment.
Bij elke stap van de kop cascaderen vorige posities naar beneden. Bij eten van een segment: nieuw segment op willekeurig punt. Botsingcontrole: itereren door segmentcoördinaten en controleren of kop-coördinaten overeenkomen. Ook botsing met muren = verlies.
Snake Spelen
Ik startte de console, wachtte tot het scherm voedsel en eerste segment laadde. Dan stuurt de joystick de slang in de juiste richting terwijl hij het voedsel opvraat. Leuk spel, geweldige tijdverdrijver in een miniatuur formaat.
Code
- ATtiny85 Code
ATtiny85 Code (C/C++)
//#include
#include
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g(U8G2_R0, /* clock=*/ 2, /* data=*/ 0, /* reset=*/ U8X8_PIN_NONE);
#define MAX_LENGTH 30
#define X 0
#define Y 1
#define JOYSTICK_X 2
#define JOYSTICK_Y 3
#define DIR_THRESH 300
uint8_t segmentPositions[MAX_LENGTH][2];
uint8_t headPosition[2] = {63, 31};
uint8_t foodPosition[2];
uint8_t tempPosition0[2];
uint8_t tempPosition1[2];
int segmentLength = 1;
void gameUpdate();
enum DIRECTIONS { RIGHT, DOWN, LEFT, UP } currentDirection;
void setup() {
//TinyWireM.begin();
u8g.begin();
u8g.setPowerSave(0);
pinMode(JOYSTICK_X, INPUT);
pinMode(JOYSTICK_Y, INPUT);
randomSeed(analogRead(0));
beginGame();
}
void loop() {
u8g.firstPage();
do {
gameUpdate();
u8g.setColorIndex(1);
} while(u8g.nextPage());
}
void beginGame(){
currentDirection = RIGHT;
spawnFood();
delay(1000);
}
bool checkCollisions(){
for(int i=1; i= 128) return 1;
else if(headPosition[Y] <= 0 || headPosition[Y] >= 64) return 1;
return false;
}
void spawnFood(){
int randomX = random(5, 123);
int randomY = random(5, 60);
foodPosition[X] = randomX;
foodPosition[Y] = randomY;
}
void checkFoodEaten(){
if(headPosition[X] == foodPosition[X] || headPosition[Y] == foodPosition[Y]){
segmentLength += 1;
spawnFood();
}
}
void updateDirection(){
int joy_x_val = analogRead(JOYSTICK_X);
int joy_y_val = analogRead(JOYSTICK_Y);
if(joy_x_val <= DIR_THRESH) currentDirection = LEFT;
else if(joy_x_val >= 1023-DIR_THRESH) currentDirection = RIGHT;
else if(joy_y_val <= DIR_THRESH) currentDirection = UP;
else if(joy_y_val >= 1023-DIR_THRESH) currentDirection = DOWN;
}
void displaySegments(){
for(int segment=0; segment < segmentLength; segment++){
u8g.drawPixel(segmentPositions[segment][X], segmentPositions[segment][Y]);
}
u8g.drawPixel(foodPosition[X], foodPosition[Y]);
}
void gameUpdate(){
updateDirection();
tempPosition0[X] = headPosition[X];
tempPosition0[Y] = headPosition[Y];
switch(currentDirection){
case UP: headPosition[Y] -= 1; break;
case DOWN: headPosition[Y] += 1; break;
case LEFT: headPosition[X] -= 1; break;
case RIGHT: headPosition[X] += 1; break;
}
// Update segmentposities relatief aan kop
for(int segment=1; segment < segmentLength; segment+= 2){
tempPosition1[X] = segmentPositions[segment][X];
tempPosition1[Y] = segmentPositions[segment][Y];
segmentPositions[segment][X] = tempPosition0[X];
segmentPositions[segment][Y] = tempPosition0[Y];
tempPosition0[X] = segmentPositions[segment+1][X];
tempPosition0[Y] = segmentPositions[segment+1][Y];
segmentPositions[segment+1][X] = tempPosition1[X];
segmentPositions[segment+1][Y] = tempPosition1[Y];
}
displaySegments();
checkFoodEaten();
if(checkCollisions()) endGame();
if(segmentLength >= MAX_LENGTH) endGame();
delay(50);
}
void endGame(){
segmentLength = 1;
headPosition[0] = 63;
headPosition[1] = 31;
beginGame();
}
Zorg dat je eerst de U8g2lib installeert.
Aangepaste Onderdelen en Behuizingen
(Bestanden beschikbaar op projectpagina)
Schema's
Productieproces
- TinyML Taaldetector met Edge Impulse op Arduino Nano 33 BLE Sense
- Bouw een real-time gyroscoop-spel met Arduino Nano & MPU-6050 sensor
- Arduino Digitale Dobbelsteen: Bouw Je Eigen LCD-willekeurige Getalgenerator
- Bouw Je Eigen 37-LED Roulette-spel met Arduino Nano
- Draagbare Afstandsdetector – Arduino Nano DIY Kit met HC-SR04, 18650 Accu en 3D-geprinte Behuizing
- Een audio-reactieve bureaulamp bouwen met Arduino Nano
- Koper Elektrolyseren Project: Bouw een Slim Systeem met Arduino UNO
- NeoPixel Matrix Pong op Arduino Nano: Bouw je eigen neon Pong-spel
- Indrukwekkende Lichtsequenties Maken met Arduino en Shift Register
- Slimme stopcontact: 120V Arduino-gebaseerde slimme uitgang met Real-Time Clock
- Arduino Beveiligingssysteem: Slimme Bewegingsdetectie & Alarm