Pollen Counter
Pollen counter kan gespeeld wordt door op 3 knoppen te drukken. Het is een rekenspel voor kinderen/mensen die graag van een (kleine) rekenuitdaging houden. Ze leren tellen met binair op een leuke wijze dmv LED lampjes en ondertussen lossen ze (lastige) sommen op.
Het doel van het spel is dat je 5 vragen achter elkaar goed beantwoordt zodat je de bijen pollen/stijfmeel kan geven om honing mee te maken. Zo wordt de speler gestimuleerd om te spelen zodat ze de bij kunnen helpen.
Ik wou iets maken wat op-zich-zelf zou kunnen staan maar ook goed vast te houden zijn zodat je sneller geneigd bent om het op te pakken en het te gebruiken. Het zou makkelijk te gebruiken moeten zijn, speelbaar met 1 hand en niet complex zijn om te begrijpen.
Supplies
Hardware
- Arduino UNO
- Breadboard
- 8 x Resistors
- 5 x Led lamp
- 3 x Push buttons
- OLED display 0.91 (https://hackerstore.nl/Artikel/1071)
- draden (lang en kort)
Extra Hardware
- Soldeertin
- Soldeerbout
- perfboard
Software
- Arduino IDE
- ADAFRUIT GFX library
Schema's
Electronisch schema van de knoppen en LED aansluiting
Electronisch schema van OLED display aansluiting (credit: https://startingelectronics.org/tutorials/arduino/... )
Om het OLED display aan te sluiten moet je het vast solderen
Wat Moet Het Doen?
Het is een rekenspel voor kinderen/mensen die graag van een (kleine) rekenuitdaging houden. Ze leren tellen met binair op een leuke wijze dmv LED lampjes en ondertussen lossen ze (lastige) sommen op.
Het doel van het spel is dat je 5 vragen achter elkaar goed beantwoordt zodat je de bijen pollen/stijfmeel kan geven om honing mee te maken. Zo wordt de speler gestimuleerd om te spelen zodat ze de bij kunnen helpen.
Dus we hebben nodig
- Een random nummer generator
- Iets wat voor ons die nummers verwerkt en in een som zet.
- iets wat het antwoord checkt en vergelijkt met ons antwoord
- Een manier vinden om ons antwoord uit te kiezen en te kunnen invoeren
- Een variabele die bij houdt hoeveel antwoorden we goed hebben
- Een manier om alles wat hierboven staat aan onze speler te laten zien
Code (totaal)
//Kris de Haas ITTT #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 32 #define NUMFLAKES 10 #define OLED_RESET 3 #define SCREEN_ADDRESS 0x3C Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); #define LOGO_HEIGHT 16 #define LOGO_WIDTH 16 static const unsigned char PROGMEM logo_bmp[] = { 0b00000000, 0b11000000, 0b00000001, 0b11000000, 0b00000001, 0b11000000, 0b00000011, 0b11100000, 0b11110011, 0b11100000, 0b11111110, 0b11111000, 0b01111110, 0b11111111, 0b00110011, 0b10011111, 0b00011111, 0b11111100, 0b00001101, 0b01110000, 0b00011011, 0b10100000, 0b00111111, 0b11100000, 0b00111111, 0b11110000, 0b01111100, 0b11110000, 0b01110000, 0b01110000, 0b00000000, 0b00110000 }; const int buttonEnter = 2; const int Vowels = 4; const int Math = 7; //LEDS int LED_Gr = 8; int LED_R = 9; int LED_B = 10; int LED_G = 11; int LED_O = 13; //Button1 int buttonPushCounter = 0; int buttonState = 0; int lastButtonState = 0; //Button2 int initial = 0; int oldstate = 0; int vowelState = 0; int Letter; //Button 3 int MathState ; int lastmathState; //Numbers long randomNumber ; long addNumber ; int totalValue; int correctAns; int pollen = 0; void setup() { Serial.begin(115200); display.clearDisplay(); pinMode(Math,INPUT); pinMode(buttonEnter, INPUT); pinMode (Vowels, INPUT); pinMode(LED_Gr,OUTPUT); pinMode(LED_R,OUTPUT); pinMode(LED_B,OUTPUT); pinMode(LED_G,OUTPUT); if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1306 allocation failed")); for(;;); } display.display(); delay(2000); display.clearDisplay(); display.drawPixel(10, 10, SSD1306_WHITE); display.display(); delay(2000); } void loop() { vowelState = digitalRead(Vowels); buttonState = digitalRead(buttonEnter); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); EnterInput(); Questions(); Numbers(); } void EnterInput(){ if (buttonState != lastButtonState) { if (buttonState == HIGH) { buttonState = digitalRead(buttonEnter); display.clearDisplay(); display.setCursor(0,0); display.println(F("Antwoord: ")); display.display(); } if(buttonState == LOW) { display.setCursor(65,0); display.setTextSize(2); display.println(Letter); display.setTextSize(1); display.display(); if (Letter != correctAns) { pollen = 0; display.setCursor(0,17); display.println ("Try again"); display.setCursor(0,24); display.println ("goede antwoord : " ); display.setCursor(100,24); display.println(correctAns); display.display(); } if (Letter == correctAns){ pollen = pollen +1; display.setCursor(0,16); display.println (" Nice!"); display.setCursor(0,24); display.println("bloemen : "); display.setCursor(75, 24); display.println(pollen); display.display(); } if (pollen == 5 ) { delay(2000 ); testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); } } delay(50); } lastButtonState = buttonState; } void Questions() { NumGen(); MathState = digitalRead(Math); if (MathState!= lastmathState){ if (MathState == HIGH) { correctAns = randomNumber - addNumber; MathState = digitalRead(Math); display.setTextSize(2 ); display.clearDisplay(); display.setCursor(0,0); display.print(randomNumber); display.print( " = "); display.print( addNumber); display.print( " + "); display.display(); } if (MathState == LOW) { display.setTextSize(1); } delay(500); } lastmathState = MathState; } void NumGen() { randomNumber = random(1,17); addNumber = random(0, randomNumber-1); } void Numbers() { if (vowelState == HIGH) { delay(50); vowelState = digitalRead(Vowels); if (vowelState == LOW) { initial = oldstate + 1; } }else {delay(100);} switch (initial){ case 1: digitalWrite(LED_Gr,HIGH); digitalWrite(LED_R,LOW); digitalWrite(LED_B,LOW); digitalWrite(LED_G,LOW); digitalWrite(LED_O,LOW); Letter = 1; oldstate = initial; break; case 2: digitalWrite(LED_Gr,LOW); digitalWrite(LED_R,HIGH); digitalWrite(LED_B,LOW); digitalWrite(LED_G,LOW); Letter = 2; oldstate = initial; break; case 3: digitalWrite(LED_Gr,HIGH); digitalWrite(LED_R, HIGH); digitalWrite(LED_B,LOW); digitalWrite(LED_G,LOW); Letter = 3; oldstate = initial; break; case 4: digitalWrite(LED_Gr,LOW); digitalWrite(LED_R,LOW); digitalWrite(LED_B,HIGH); digitalWrite(LED_G,LOW); Letter = 4; oldstate = initial; break; case 5: digitalWrite(LED_Gr,HIGH); digitalWrite(LED_R,LOW); digitalWrite(LED_B,HIGH); digitalWrite(LED_G,LOW); Letter = 5; oldstate = initial; break; case 6: digitalWrite(LED_Gr,LOW); digitalWrite(LED_R,HIGH); digitalWrite(LED_B,HIGH); digitalWrite(LED_G,LOW); Letter = 6; oldstate = initial; break; case 7: digitalWrite(LED_Gr,HIGH); digitalWrite(LED_R,HIGH); digitalWrite(LED_B,HIGH); digitalWrite(LED_G,LOW); Letter = 7; oldstate = initial; break; case 8: digitalWrite(LED_Gr,LOW); digitalWrite(LED_R,LOW); digitalWrite(LED_B,LOW); digitalWrite(LED_G,HIGH); Letter = 8; oldstate = initial; break; case 9: digitalWrite(LED_Gr,HIGH); digitalWrite(LED_G,LOW); digitalWrite(LED_B,LOW); digitalWrite(LED_G,HIGH); Letter = 9; oldstate = initial; break; case 10: digitalWrite(LED_Gr,LOW); digitalWrite(LED_R,HIGH); digitalWrite(LED_B,LOW); digitalWrite(LED_G,HIGH); Letter = 10; oldstate = initial; break; case 11: digitalWrite(LED_Gr,HIGH); digitalWrite(LED_R,HIGH); digitalWrite(LED_B,LOW); digitalWrite(LED_G,HIGH); Letter = 11; oldstate = initial; break; case 12: digitalWrite(LED_Gr,LOW); digitalWrite(LED_R,LOW); digitalWrite(LED_B,HIGH); digitalWrite(LED_G,HIGH); Letter = 12; oldstate = initial; break; case 13: digitalWrite(LED_Gr,HIGH); digitalWrite(LED_R,LOW); digitalWrite(LED_B,HIGH); digitalWrite(LED_G,HIGH); Letter = 13; oldstate = initial; break; case 14: digitalWrite(LED_Gr,LOW); digitalWrite(LED_R,HIGH); digitalWrite(LED_B,HIGH); digitalWrite(LED_G,HIGH); Letter = 14; oldstate = initial; break; case 15: digitalWrite(LED_Gr,HIGH); digitalWrite(LED_R,HIGH); digitalWrite(LED_B,HIGH); digitalWrite(LED_G,HIGH); Letter = 15; oldstate = initial; break; case 16: digitalWrite(LED_Gr,LOW); digitalWrite(LED_R,LOW); digitalWrite(LED_B,LOW); digitalWrite(LED_G,LOW); digitalWrite(LED_O,HIGH); Letter = 16; oldstate = initial; break; default: digitalWrite(LED_Gr,LOW); digitalWrite(LED_R,LOW); digitalWrite(LED_B,LOW); digitalWrite(LED_G,LOW); Letter = 0; oldstate = 0; break; } } #define XPOS 0 #define YPOS 1 #define DELTAY 2 void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) { int8_t f, icons[NUMFLAKES][3]; for(f=0; f< NUMFLAKES; f++) { icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); icons[f][YPOS] = -LOGO_HEIGHT; icons[f][DELTAY] = random(1, 6); Serial.print(F("x: ")); Serial.print(icons[f][XPOS], DEC); Serial.print(F(" y: ")); Serial.print(icons[f][YPOS], DEC); Serial.print(F(" dy: ")); Serial.println(icons[f][DELTAY], DEC); } for(;;) { display.clearDisplay(); for(f=0; f< NUMFLAKES; f++) { display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE); } display.display(); delay(200); for(f=0; f< NUMFLAKES; f++) { icons[f][YPOS] += icons[f][DELTAY]; if (icons[f][YPOS] >= display.height()) { // Reinitialize to a random position, just off the top icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); icons[f][YPOS] = -LOGO_HEIGHT; icons[f][DELTAY] = random(1, 6); } } } }
Code Uitleg
In chronologische volgorde per foto:
- Voor void setup: Hier introduceer ik de ADAFRUIT GFX library, wordt de hoogte (SCREEN_HEIGHT) en breedte (SCREEN_WIDTH) in pixels gedefinieert.
- Lijst van variabelen die ik later ga gebruiken
- Letter = eigen nummer input
- void setup : Code die 1x wordt afgespeeld, als het niet werkt geeft het een error bericht, zo wel dan staat er 1 witte pixel op het scherm
- void loop : Ik zet de standaard tekst grootte en kleur want ik wil dat maar 1x te hoeven aangeven voor heel de code.
- Ook wordt de staat van knop 2 en 3 gecheckt
- void EnterInput ; De staat van de knop (ingedrukt of niet) wordt afgelezen
- ingedrukt: display "antwoord" op scherm
- omhoog: checkt of je antwoord correct is
- Incorrect: Reset je pollen, geeft aan dat jouw antwoord incorrect is en geeft het goede antwoord
- Correct: Laat zien hoeveel pollen(bloemen) je hebt, positief woordje en telt een extra pol bij je de hoeveelheid pol op die je hebt (0-4)
- Als je 5 pollen hebt (5 antwoorden goed achter elkaar) dan wordt een animatie opgeroepen
- void Questions : Random nummers uit de NumGen wordt opgehaald en weergegeven op de OLED display.
- Zet tekst grootte eerst op 2 als de knop is ingedrukt en weer op 1 als de knop weer omhoog gaat
- void NumGen; genereert een randomnummer tussen de 1 en 17 (randomNumber)
- Genereert een nummer tussen 1 en randomNumber - 1 zodat het altijd iets hebt om op te tellen (addNumber)
- (t/m 13) void Numbers : Hardcode de verandering van de int "Letters" (nummer die de speler zelf kan invoeren), wordt aangetoond door LEDS die aan en uit gaan om aan te geven op welk nummer je zit. De break zit er in zodat < initial = oldstate + 1 > niet wordt herhaald zodat er niet wordt doorgeteld zonder de input van de speler.
14. void testanimate : geeft begin positie aan van de Numflakes.
- voor een oneindige loop blijft het de tekening herhalen met de nieuwe posities die elke frame wordt ge-update. Als de positie onder het scherm komt te zitten wordt er een nieuwe positie boven het scherm gegenereerd.
Iteratie 1
Eerst had ik het idee om een keyboard te maken die maar 3-4 vingers nodig zou hebben om te kunnen besturen maar daarvoor had ik een Arduino Leonardo nodig en ik wou het graag bij de Arduino Uno houden.
Omdat er meer letters zijn in het alfabet dan mensen vingers hebben had ik bedacht om een simpel systeem te maken die door een combinatie van knoppen letters zou produceren (1 = a, 2 = e, 12 = h). Omdat ik maar 4 push buttons had moest ik wel gebruik maken van een relay systeem om met 1 knop meerdere opties te kunnen uitkiezen.
Video van de relay die ik heb gebruikt
Iteratie 2-3
2e prototype
Ik had al best wel snel het idee om iets met getallen te gaan werken omdat ik een bepaald programma moest gebruiken om een keyboard library te instaleren maar het lukte niet.
Voor mijn 2e prototype had ik een 3 knoppen systeem gemaakt
1. Enter getal die je met de 2e knop invoert
2. Tellen van 1 tot 10 (aangegeven door LED's die in een relay zijn gezet)
3. Print een som op het scherm Alles werd geprint op de serial monitor van Arduino IDE.
3e prototype (VIDEO 1: COM4)
Alles is hetzelfde als prototype 2 maar ik heb het tel systeem naar binair veranderd. Je telt nu hoe je binair zou tellen op je vingers (1-2-4-8-16). Dus de sommen hebben nu ook de mogelijkheid om groter te worden dan 11.
Het programma rekent nu ook zelf het antwoord van de gegeven som uit en checkt of jouw antwoord overeenkomt met de uitkomst. Het geeft een compliment als je het goed hebt en zegt dat je het nog een keer moet/ kan proberen als het niet klopt. De volgorde van het opgeven van de som en het uitrekenen was wel belangrijk want anders kreeg ik een som maar rekende het zelf een andere som uit.
Het antwoord wordt gecheckt in void Questions met de getallen die uit void NumGen komen.
Heb ook alles in functies gezet ipv in de void loop voor overzichtelijkheid
Iteratie 4-5
4e prototype
Heb de OLED aangesloten op de Arduino en display het nu op de OLED en niet meer op de Serial Monitor.
Voor de omhulsing had ik het idee om het te maken dat je plat kan neer zetten (Arduino staat achter de knoppen) maar ook dat je het in je gemakkelijk in je hand zou kunnen vast houden (Arduino staat onder de knoppen). Ik dacht eerst dat een stoffen waaier handig zou zijn omdat stof flexibel is dus je kan het openslaan. Ik had veel moeite met het vinden van een vorm die goed werkte want ik wou niet dat de stof te bol zou worden as het de Arduino gewoon plat zou staan maar het moest wel 90 graden open slaan. Ook zouden de kabels minder op elkaar gedrukt worden (minder kans op kabel schade).
5e prototype (VIDEO 2)
Omdat stof niet lukte had ik bedacht dat het makkelijker zou zijn om een deksel te maken die je op meerdere manier kan vouwen/buigen om zich aan de vorm te passen zodat de kabels niet zichtbaar zouden zijn als je hem in 1 van de 2 standen zou zetten. Dit werkte helaas alleen bij bepaalde afmetingen van van beide bakken waar de breadboard met knoppen, en Arduino in zaten. Toen ik de bakken passender wou maken liep ik tegen het probleem aan dat de zijkanten heel ongelijk werden want de breedte werd de hoogte en vise versa.
Iteratie 6
6e prototype
Ben gegaan van Breadboard naar perfboard. Heb ook de omhulsing veder gedecoreerd met wat vilt om gras te simuleren.
7e, Laatste Iteratie
De draden zijn gekleurd code en gesoldeerd, alles kan uit elkaar worden gehaald.
Er zijn haken aan de achterkant zodat het middenstuk er af kan worden gehaald en daarna er weer op kan worden gezet.
De buiten kant is geverfd en gedecoreerd in de stijl van een bijenhuisje,
Heb de variabele pollen en de < testanimate > functie toegevoegd en geïmplementeerd dat bij 5 pollen de functie aanslaat en als pollen < 5 dat het niks doet en de code nog steeds kan worden afgespeeldn.
Reflectie
Ik heb geleerd hoe je variabelen in verschillende functies kan benuttigen, een relay van LED's kan besturen met 1 knop. Het werken met functies heeft me heel erg geholpen om overzichtelijkere code te schrijven dus ik ben blij dat ik functies heb leren gebruiken. Bij Arduino is het niet zo geweldig belangrijk in welke volgorde je je code zet maar ik heb wel ontdekt dat bij sommige variabelen (NumGen; randomNumber en addNumber) het zeker belangrijk is omdat het elke keer ge-update wordt en je wilt eigenlijk constant dezelfde variabele gebruiken als de code 1x wordt afgespeeld.
Het bedenken en uitwerken van een flexibel ontwerp was ook leuk en interessant. De deksel designen was niet moeilijk maar het aanpassen zodat het ook bruikbaar is op verschillende maten is iets wat ik nog zou moeten onderzoeken.
Ook heb ik nu solderen beter onder die knie en denk wel dacht mocht ik ooit nog zo iets zoals dit moeten maken waarbij ik draden moet solderen dat het me beter wel af zou gaan.