Spis treści:
Większość z nas pamięta z dzieciństwa grę Snake. Była ona dostępna jako wbudowana aplikacja w telefonach fińskiego koncernu Nokia. Gra bez wątpienia jest kultowa, jednak po co instalować ją na smartfonie? To proste – bo jest kultowa. Zbudujmy dziś retro konsolę, na której będziemy mogli sterować wężem zjadającym jabłka.
Co będzie podstawą naszego projektu?
Nasza konsola oparta będzie o płytkę uruchomieniową Arduino Pro Mini i wyświetlacz OLED, cztery przyciski, przełącznik ON-OFF przetwornicę step-up/step-down, koszyk na dwie baterie AAA, same baterie i obudowę wydrukowaną w 3D. Dlaczego zdecydowaliśmy się na Pro Mini i wyświetlacz OLED? Głównie dlatego, że zajmują mało miejsca i ze względu na małe rozmiary pochłaniają mniej energii elektrycznej. Dodatkowo ekran OLED nie ma stałego podświetlenia, co pozwala na jeszcze większą oszczędność prądu. Dokładna lista podzespołów znajduje się poniżej:
- Arduino Pro Mini 5V
- Programator UART-USB
- Wyświetlacz OLED
- 4x Przełącznik ON-OFF chwilowy
- Przełącznik ON-OFF bistabilny
- Koszyk na baterie 2xAAA
- Przetwornica step-up/step-down 5V
- Baterie AAA
- Klej cyjanoakrylowy
- Koszulki termokurczliwe
- Przewody
- Śrubki
- Przełącznik ON-OFF bistabilny
- Koszyk na baterie 2xAAA
- Przetwornica step-up/step-down 5V
- Baterie AAA
- Klej cyjanoakrylowy
- Koszulki termokurczliwe
- Przewody
- Śrubki
Tworzenie prototypu na płytce stykowej
Do sprawdzenia funkcjonalności urządzenia warto wykonać prototyp. Jak na miłośników Arduino przystało, zbudujemy go na płytce stykowej. Jeśli mamy pod ręką płytkę Arduino UNO, to z pewnością dużo łatwiej będzie nam testować poszczególne rozwiązania, a jeśli nie, to możemy wpiąć Pro Mini w płytkę stykową i programować przy pomocy konwertera UART-USB. Duże przyciski zastąpimy małymi tact-switchami. Układ musimy zbudować tak, jak na schemacie poniżej.
Rezystory których użyliśmy mają wartość 10kOhm. Przyciski są podłączone w kolejności:
1. Przycisk w prawo – pin 7
2. Przycisk w dół – pin 5
3. Przycisk w górę – pin 4
4. Przycisk w lewo – pin 6
Nasz wyświetlacz komunikuje się przez magistralę I2C, więc poza podłączeniem zasilania musimy podłączyć piny SDA i SCL. Pin SDA wyświetlacza łączymy z pinem A4 płytki, a pin SCL z pinem A5.
Jak programować? Aby prawidłowo zrobić to prawidłowo, musimy podłączyć do płytki programator. Na samym początku podłączamy piny zasilania, następnie łączymy pin TX Arduino z RX programatora, a pin RX Pro Mini z TX konwertera. Teraz możemy podłączyć przewód USB do programatora i uruchomić program Arduino IDE. Kod, który wgramy do naszej płytki, dostępny jest w poniżej.
#include<SPI.h>
#include<Adafruit_GFX.h>
#include<Adafruit_SSD1306.h>
Adafruit_SSD1306 display(128,64);
#define INTPIN 3
#define UPPIN 4
#define DWNPIN 5
#define LFTPIN 6
#define RHTPIN 7
#define SND 9
#define DIRUP 1
#define DIRDOWN 2
#define DIRLEFT 3
#define DIRRIGHT 4
volatile uint8_t buttonpressed=0;
bool butup=0;
bool butdown=0;
bool butleft=0;
bool butright=0;
byte snakePosX[30];
byte snakePosY[30];
int snakeX=30;
int snakeY=30;
int snakeSize=1;
uint8_t worldMinX=0;
uint8_t worldMaxX=128;
uint8_t worldMinY=10;
uint8_t worldMaxY=63;
bool scranAte =0;
uint8_t scranPosX=0;
uint8_t scranPosY=0;
long playscore=0;
long highscore=30;
void interruptpressed()
{
delay(150);
updatedirection();
}
void updatedirection()
{
butup=digitalRead(UPPIN);
butdown=digitalRead(DWNPIN);
butleft=digitalRead(LFTPIN);
butright=digitalRead(RHTPIN);
if(butup==true)
{
buttonpressed=DIRUP;
butup=false;
tone(SND,1500,10);
}
if(butdown==true)
{
buttonpressed=DIRDOWN;
butdown=false;
tone(SND,1500,10);
}
if(butleft==true)
{
buttonpressed=DIRLEFT;
butleft=false;
tone(SND,1500,10);
}
if(butright==true)
{
buttonpressed=DIRRIGHT;
butright=false;
tone(SND,1500,10);
}
}
void updateDisplay()
{
display.fillRect(0,0, display.width()-1,8,BLACK);
display.setTextSize(0);
display.setTextColor(WHITE);
display.setCursor(2,1);
display.print("Score:");
display.print(String(playscore, DEC));
display.setCursor(66,1);
display.print("High:");
display.print(String(highscore ,DEC));
display.drawLine(0,0,127,0,WHITE);
display.drawLine(63,0,63,9,WHITE);
display.drawLine(0,9, 127,9,WHITE);
display.drawLine(0,63,127,63,WHITE);
display.drawLine(0,0,0,63,WHITE);
display.drawLine(127,0,127,63,WHITE);
}
void updateGame()
{
display.clearDisplay();
display.drawPixel(scranPosX,scranPosY, WHITE);
scranAte = scranFood();
if (outOfArea()||selfCollision())
{
gameOver();
}
for(int i=0;i<snakeSize;i++)
{
display.drawPixel(snakePosX[i],snakePosY[i],WHITE);
}
for(int i=snakeSize;i>0;i--)
{
snakePosX[i] = snakePosX[i-1];
snakePosY[i] = snakePosY[i-1];
}
if(scranAte)
{
snakeSize+=1;
snakePosX[snakeSize-1]=snakeX;
snakePosY[snakeSize-1]=snakeY;
}
switch(buttonpressed)
{
case DIRUP:
snakeY-=1;
break;
case DIRDOWN:
snakeY+=1;
break;
case DIRLEFT:
snakeX-=1;
break;
case DIRRIGHT:
snakeX+=1;
break;
}
snakePosX[0] = snakeX;
snakePosY[0] = snakeY;
updateDisplay();
display.display();
}
void placeScran()
{
scranPosX=random(worldMinX+1,worldMaxX-1);
scranPosY=random(worldMinY+1,worldMaxY-1);
}
bool scranFood()
{
if(snakeX==scranPosX&&snakeY==scranPosY)
{
playscore=playscore+10;
tone(SND,2000,10);
updateDisplay();
placeScran();
return 1;
}
else
{
return 0;
}
}
bool outOfArea()
{
return snakeX <= worldMinX||snakeX >=worldMaxX ||snakeY<=worldMinY|| snakeY>=worldMaxY;
}
void gameOver()
{
uint8_t rectX1,rectY1,rectX2,rectY2;
rectX1=38;
rectY1=28;
rectX2=58;
rectY2=12;
display.clearDisplay();
display.setCursor(40,30);
display.setTextSize(1);
tone(SND,2000,50);
display.print("GAME ");
tone(SND,1000,50);
display.print("OVER");
if(playscore>=highscore)
{
highscore=playscore;
}
for(int i=0;i<=16;i++)
{
display.drawRect(rectX1,rectY1,rectX2,rectY2,WHITE);
Serial.println("if loop");
display.display();
rectX1-=2;
rectY1-=2;
rectX2+=4;
rectY2+=4;
tone(SND,i*200,3);
}
display.display();
rectX1=0;
rectY1=0;
rectX2=0;
rectY2=63;
for (int i =0;i<=127;i++)
{
uint8_t cnt=0;
display.drawLine(rectX1,rectY1,rectX2,rectY2,BLACK);
rectX1++;
rectX2++;
display.display();
}
display.clearDisplay();
playscore=0;
snakeSize=1;
snakeX=display.width()/2;
snakeY=display.height()/2;
waitForPress();
}
void waitForPress()
{
bool waiting=0;
display.clearDisplay();
while(waiting==0)
{
drawALineForMe(WHITE);
drawALineForMe(BLACK);
display.fillRect(19,20,90,32,BLACK);
display.setTextColor(WHITE);
display.setCursor(35,25);
display.setTextSize(2);
display.println("SNAKE");
display.drawRoundRect(33,22,62,20,4,WHITE);
display.drawRect(19,20,90,32,WHITE);
display.setCursor(25,42);
display.setTextSize(0);
display.println("press any key");
display.fillRect(0,0,127,8,BLACK);
display.setCursor(10,0);
display.print("High Score :");
display.print(highscore);
display.display();
waiting = digitalRead(INTPIN);
buttonpressed=0;
}
}
void drawALineForMe(uint8_t clr)
{
uint8_t line1X,line1Y,line2X,line2Y=0;
line1X = random(worldMinX+1,worldMaxX-1);
line1Y = random(worldMinY+1,worldMaxY-1);
line2X = random(worldMinX+1,worldMaxX-1);
line2Y = random(worldMinY+1,worldMaxY-1);
display.drawLine(line1X,line1Y,line2X,line2Y,clr);
}
bool selfCollision()
{
for(byte i=4;i<snakeSize;i++)
{
if (snakeX==snakePosX[i]&&snakeY==snakePosY[i])
{
tone(SND,2000,20);
tone(SND,1000,20);
return 1;
}
}
return 0;
}
void setup()
{
delay(100);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(WHITE);
display.setRotation(0);
display.setTextWrap(false);
display.dim(0);
pinMode(INTPIN,INPUT);
pinMode(UPPIN,INPUT);
pinMode(DWNPIN,INPUT);
pinMode(LFTPIN,INPUT);
pinMode(RHTPIN,INPUT);
attachInterrupt(digitalPinToInterrupt(INTPIN),interruptpressed,RISING);
waitForPress();
placeScran();
}
void loop()
{
updateGame();
}
Teraz standardowo wybieramy płytkę Pro Mini i wybieramy port, do którego wpięty jest nasz programator. Po zatwierdzeniu klikamy na przycisk “Wgraj”. Jeśli po wgraniu uruchomi się gra, to przechodzimy do następnego kroku, jakim jest…
Budowa przenośnej konsoli do gry
Po sprawdzeniu funkcjonalności na płytce stykowej możemy przejść do budowy samej konsoli, zacznijmy od wydrukowania obudowy. Plik w formacie stl dostępny jest na naszym profilu w witrynie Thingiverse. Ja wydrukowałem swoją obudowę 3D w technologii DLP z częściowo transparentnego materiału.
Tworzenie końcowego urządzenia zaczynamy od przylutowania przewodów do przycisków i zabezpieczenia ich przy pomocy koszulek termokurczliwych. Piny przycisków są duże, więc bardzo łatwo będzie je przylutować. Od każdego pinu powinien wychodzić przewód o długości minimum 5 cm. Możemy je zabezpieczyć przy pomocy koszulek termokurczliwych.
Jak oceniasz ten wpis blogowy?
Kliknij gwiazdkę, aby go ocenić!
Średnia ocena: 4.8 / 5. Liczba głosów: 4
Jak dotąd brak głosów! Bądź pierwszą osobą, która oceni ten wpis.



Jedna odpowiedź
Spodobał mi się twój pomysł. Podzielił byś się plikiem obudowy?