Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

ATmega Alien thema-gokautomaat

Componenten en benodigdheden

Microchip-technologie ATmega328
ATmega328P-PU, om precies te zijn.$3.00 Een voor de SlotMachine, een voor het I2C-tegoed LED-display slaaf.
× 2
Weergave met zeven segmenten met 8 cijfers
$ 1,20 Om het tegoed van de speler weer te geven.
× 1
8x8 matrix, 4 segmenten, MAX7219
$ 3,78 Om draaiende rollen te simuleren en de symbolen weer te geven. Slechts drie van de vier segmenten worden gebruikt.
× 1
I2C 2004 Serial Blue Backlight LCD-module 20 X 4 2004
$ 3,00 Voor het weergeven van het menu met opties. Winkel rond op aliexpress. Betaal geen verzendkosten!
× 1
Broodplank (algemeen)
830 punten$ 4,00
× 2
Kortstondige contactknoppen
$ 1,00 voor 50. Eén regelt het draaien van de rollen, drie om door het menu te navigeren, twee om pin 1 te aarden van de ATMegas.
× 6
RGB diffuse gemeenschappelijke kathode
Gebruikt om verschillende dingen aan te geven.
× 1
LED (generiek)
Geeft aan of de boards van stroom worden voorzien.
× 1
Weerstand 10k ohm
4 om elk van de knoppen omhoog te trekken, 2 voor pin 1 van de ATMega's.
× 6
Weerstand 1k ohm
Tussen elk van de knoppen en de ATmega-invoerpinnen.
× 4
Weerstand 330 ohm
Voor de rode, groene en blauwe draden van de RGB-led.
× 3
16 MHz kristal
Een voor de ATmega328P-PU van de SlotMachine en een voor de ATmega328P-PU van de LED-displayslave. Beide werken op 16MHz.
× 2
Schuifschakelaar
Voor de voeding.
× 1
Zoemer
Er zijn twee nodig, één voor de SlotMachine-chip en één voor de display-slave-chip. Het zou leuk zijn om de schakeling zo aan te passen dat er maar één nodig is en door beide microcontrollers kan worden gedeeld.
× 2
Condensator 22 pF
× 4
0.10 uF-condensator
× 6
Condensator 100 nF
Dit is optioneel en alleen nodig als je de Arduino Mini USB seriële adapter gebruikt voor het programmeren van de SlotMachine chip, zoals ik heb gedaan.
× 1
Condensator 10 µF
Om de voedingsspanning af te vlakken.
× 2
Lineaire regelaar (7805)
Om de voedingsspanning te regelen, 5V.
× 1
Jumperdraden (algemeen)
Je hebt hier een behoorlijke hoeveelheid van nodig. Voor het grootste deel maak ik mijn eigen, maar ik gebruik ook de jumperdraden.
× 1
Een 5v-voeding
× 1
Arduino Mini USB seriële adapter
$13,20 Dit is optioneel, je kunt je Arduino Uno gebruiken om de ATmega 328p-pu-chips te programmeren.
× 1
FTDI USB naar TTL seriële adapter
$ 1,66 x 2 =$ 3,32 Voor het programmeren van de ATmega328P-PU's. Niet afgebeeld in het schema.
× 1
Soldeerbare breadboard
Een soldeerbaar breadboard op ware grootte.
× 1
SparkFun Soldeerbaar Breadboard - Mini
× 1
Zaksoldeer - 60/40 Rosin Core 0.031" diameter
× 1
Doorzichtige plastic waterdichte elektronische projectdoosbehuizing
$ 13,00 Dit is de bijlage.
× 1

Benodigde gereedschappen en machines

Soldeerbout (algemeen)
Helpende handen

Apps en online services

Arduino IDE
Timer Free Tone-bibliotheek
LED-bedieningsbibliotheek
LiquidCrystal/LCD-bibliotheek
LiquidCrystal I2C-bibliotheek

Over dit project

ATmega Alien thema gokautomaat

Dit project is mijn implementatie van een gokautomaat met een buitenaards thema met behulp van twee ATmega328P-PU microcontrollers. Ik werd geïnspireerd door Cory Potter's Alien Invasion-gokautomaat en ik wilde dat idee uitbreiden. De gokautomaat is alleen bedoeld voor entertainment en educatieve doeleinden. Ik heb mijn best gedaan om het spel zo dicht mogelijk bij een echte gokautomaat te laten lijken. Het project is momenteel brood aan boord. Een behuizing zal worden toegevoegd zodra de onderdelen uit China komen en ik heb de kans gehad om alles op te solderen. Het duurde ongeveer twee maanden voordat ik het project in mijn vrije tijd kon bouwen. Het moeilijkste deel van de build voor mij was het begrijpen van alle wiskunde die nodig was om het spel te laten gedragen zoals de casino-industrie zou verwachten dat een eenvoudige gokautomaat zich zou gedragen na een half miljard simulaties.

Hoe het spel werkt

Het spel heeft drie rollen met dezelfde unieke 25 symbolen op elke rol (een van de 8x8-matrices op het onderdeel met 4 8x8-matrices wordt niet gebruikt). Er zijn vijf verschillende manieren om te winnen. Als je drie ruimteschepen krijgt, win je de jackpot. Als je een of twee ruimteschepen krijgt, win je ook wat credits. Als je twee of drie symbolen krijgt die bij elkaar passen, win je ook. Als je een ruimteschip en twee bijpassende symbolen krijgt, zoals hieronder afgebeeld, betaalt het spel uit op basis van het winnende evenement met de laagste kans/hoogste uitbetaling; met andere woorden, winnende evenementen sluiten elkaar uit, je kunt niet op twee verschillende manieren winnen met een enkele draai aan de rollen. Dit hield de programmering een beetje eenvoudiger. Er waren genoeg andere uitdagingen voor mij.

Functies

De gokautomaat heeft verschillende interessante functies die toegankelijk zijn via het 20 x 4 I2C-compatibele LCD-scherm met behulp van twee navigatieknoppen en een selectieknop. De knoppen gebruiken een redelijk geavanceerd de-bouncing-algoritme dat profiteert van de externe interrupt-mogelijkheid van de microcontroller. Dit is het hoofdmenu.

Omdat het menu zes regels bevat, moet je met de knop 'navigeer naar beneden' naar beneden scrollen om het hele menu te zien. Er is een knop gewijd aan het 'spinnen' van de rollen. Daarnaast kun je in het hoofdmenu ook 'Afspelen' selecteren. Je kunt je inzet op elk moment wijzigen.

De meest opwindende eigenschap is dat het spel in 'auto'-modus kan worden gespeeld; d.w.z. u selecteert de optie automatische modus in het instellingenmenu op het LCD-scherm en het spel speelt steeds opnieuw totdat u de optie opnieuw selecteert of er 1 miljoen keer is gespeeld. Dit is een cruciale functie voor het testen van het spel. Je kunt het geluid hier ook uitschakelen.

Via het menu op het LCD-scherm is het ook mogelijk om alle metrieken te bekijken die uit de simulatie zijn gegenereerd. Deze meetwaarden worden ook uitgevoerd en kunnen worden bekeken in de seriële monitor als u uw microcontroller op de monitor aansluit via de RX- en TX-pinnen met behulp van een USB-kabel. De lijst met weergegeven statistieken omvat uw tegoed, het aantal keren dat u de jackpot heeft gewonnen en het aantal keren dat u op een andere manier credits heeft gewonnen. Hierdoor kon ik simulaties uitvoeren op basis van de verschillende uitbetalingen, en dit was handig voor het opstellen en bewijzen van de uitbetalingstabel. De uitbetalingstabel zelf is niet configureerbaar; als het eenmaal is ingesteld, moet het hetzelfde blijven. Ik veronderstel dat het mogelijk zou zijn om de volatiliteitsindex configureerbaar te maken door deze te gebruiken om de uitbetalingstabellen aan te sturen, maar dat zou veel meer werk vergen.

Met de Reset-optie kunt u alle meetwaarden (behalve EEprom-schrijfbewerkingen) terugzetten naar nul. De chip zal werken voor ongeveer 100.000 schrijfacties naar EEprom. Aangezien er 512k EEprom beschikbaar is op de chip, en we gebruiken slechts een fractie daarvan, zou het mogelijk zijn om de locatie van de metrieken in EEprom daadwerkelijk te verplaatsen naarmate we de 100.000 schrijfbewerkingen naderen. Ik heb deze functie niet geïmplementeerd, maar het zou een manier zijn om de levensduur van de chip te verlengen.

Ten slotte kan het bedrag, of het percentage van elke inzet dat door het huis (in de loop van de tijd) wordt gehouden, worden geconfigureerd. Onthoud dat na het uitvoeren van een Reset-bewerking de wachtstand opnieuw moet worden ingesteld.

Het tegoed van de speler wordt altijd weergegeven in een display met acht cijfers en zeven segmenten.

De wiskunde

Er is veel werk verzet om ervoor te zorgen dat het spel realistisch was. De kansen zijn berekend en de uitbetalingstabel is zo ontworpen dat het spel een acceptabele Volatility Index (VI) heeft. Deze index meet hoe voorspelbaar het gedrag van de machine is. Een machine met een hogere VI zal de speler (of het huis) meer geld opleveren. Het is minder voorspelbaar dan een machine met een lagere VI. Het is waar dat exact hetzelfde spel zal bestaan ​​in verschillende casino's (of zelfs hetzelfde casino) met verschillende VI's. De VI wordt gewijzigd door het uitbetalingsschema te manipuleren. Voor ons spel zijn hier de kansen en uitbetalingen voor elk soort overwinning.

Merk op dat de kansen (uiterst rechts) en de uitbetaling (uiterst links) dramatisch verschillend zijn. Als dit spel zo was geprogrammeerd dat de uitbetalingstabel overeenkwam met of nauw aansluit bij de kansen, zou de VI onaanvaardbaar hoog zijn. De inhouding wordt berekend als een percentage van de uitbetaling en is het deel van een weddenschap dat door het huis/casino wordt gehouden. Zoals gezegd kun je de hold instellen via het LCD menu. Houd er rekening mee dat verschillende jurisdicties verschillende voorschriften hebben die de maximale opslag voor gokautomaten in dat rechtsgebied regelen. Een typische maximale hold is 15%. Begrijp dat het instellen van het maximum dat door de wet is toegestaan, niet noodzakelijkerwijs de winst maximaliseert die door die machine wordt gegenereerd, omdat een hogere hold spelers kan ontmoedigen om de machine te gebruiken. Ik vermoed echter dat veel spelers het ruim negeren, dat doorgaans in kleine lettertjes wordt begraven, en dat de vraagcurve voor een machine relatief verticaal is (wat betekent dat de kosten van het gebruik van de machine, het ruim, grotendeels worden genegeerd), en die winst die door de machine wordt gegenereerd, is veel meer afhankelijk van de locatie of plaatsing van de machine en van het ontwerp van het spel zelf. Maar dat is slechts speculatie. Ik weet zeker dat er enkele slimme gokkers zijn die gevoelig zijn voor het ruim.

De spreadsheet, beschikbaar met de code, met drie tabellen is gebouwd om te bewijzen dat het spel correct werkt (de eerste tabel staat hierboven). De eerste stap bij het maken van de spreadsheet was het nauwkeurig berekenen van de kansen op elk type overwinning (de kolommen Berekende waarschijnlijkheid).

Drie ruimteschepen

De kans dat er drie ruimteschepen verschijnen is het omgekeerde van het totale aantal mogelijke combinaties. Het aantal winnende combinaties, één, over het totale aantal mogelijke combinaties, 15625. Er zijn 25 unieke symbolen op elke rol, dus de kans is 1 / (25 x 25 x 25), of 0,000064. Dat maakt de kansen, 1/waarschijnlijkheid - 1, gelijk aan 1 tot 15624. Ik heb hier geleerd hoe ik de kansen kan berekenen uit de kans.

Overeenkomst met drie symbolen (behalve ruimteschepen)

De kans dat drie symbolen, behalve de ruimteschepen, overeenkomen is 24 (het aantal unieke symbolen op elke rol minus de ruimteschepen) gedeeld door het aantal mogelijke combinaties. 24 is de teller omdat er 24 combinaties van drie symbolen overeenkomen. 24/15625 =0,001536. Dat maakt de odds ongeveer 1 op 650,04.

Twee ruimteschepen

Er zijn 24 x 3 totale combinaties van twee ruimteschepen die bij elkaar passen. Dat komt omdat er drie manieren zijn om twee combinaties van een ruimteschip te maken. Geef X =ruimteschip en Y =elk ander symbool, XXY, XYX en YXX. Er zijn 24 mogelijke waarden voor Y. Dus 24 X 3 / 15625 =0,004608. De kansen zijn 1 tot 216,01.

Eén ruimteschip verschijnt

Voor elke rol zijn er 24 x 24 combinaties mogelijk voor een enkel ruimteschip dat verschijnt.

Een ruimteschip kan op elke rol verschijnen, dus je moet het aantal beschikbare combinaties op een enkele rol vermenigvuldigen met drie rollen. Dus de kans is 24 x 24 x 3 / 15625 =0,110592. Kansen zijn 1 tot 8,04.

Twee symbolen komen overeen

Voor elk gegeven twee symbolen, behalve de ruimteschepen, zijn er 23 (25 minus één ruimteschip minus één symbool waardoor het een match met drie symbolen zou zijn) x 3 rollen x 24 symbolen die geen ruimteschepen zijn. De kans is (23 X 3 X 24)/15625 =0,105984. Kansen zijn 1 tot 8,44.

Nu ik de kansen voor elk soort overwinning heb, kan ik de spreadsheet gebruiken om de uitbetalingstabel zo te ontwerpen dat de volatiliteitsindex acceptabel is (<~20). Om te begrijpen hoe ik dit moest doen, vertrouwde ik sterk op dit bericht. Ik heb waarden ingevoerd in de kolom Huisinkomen van de eerste tabel, met een proces van vallen en opstaan, totdat de VI onder de 20 was en het totaal in cel J10 zo dicht mogelijk bij nul was als ik het kon krijgen. Met behulp van die waarden heb ik THREE_SPACESHIP_PAYOUT, THREE_SYMBOL_PAYOUT, TWO_SPACESHIP_PAYOUT, ONE_SPACESHIP_PAYOUT en TWO_SYMBOL_PAYOUT in SlotMachine.ino dienovereenkomstig ingesteld. Vervolgens heb ik, door eerst een vastlegging van nul procent te gebruiken, vijf simulaties van 1.000.001 spelen uitgevoerd en de waarden uit het metrische menu ingevoerd in de juiste rijen en kolommen in de tabel met werkelijke resultaten (de derde tabel).

Ik merkte op dat de werkelijke waarschijnlijkheden nauw overeenkwamen met de berekende kansen, en dat de kolom Pct Diff Prob redelijk was. Ik heb ook de waarden in de rij 'Huis betaalt' vergeleken met het bereik van waarden uit de kolommen Hoog inkomen en Laag inkomen van de rij 1.000.000 van de tabel Inzicht in potentieel inkomen (de tweede tabel), en merkte op dat de waarden van de tabel met werkelijke resultaten viel binnen het bereik dat is gespecificeerd door de kolommen Hoog inkomen en Laag inkomen. De tabel Inzicht in potentieel inkomen definieert het verwachte inkomensbereik voor een bepaalde houdwaarde met een betrouwbaarheidsinterval van 90%. In het onderstaande voorbeeld is de wachtstand ingesteld op 0, dus de kans op winnen is gelijk aan de kans op verliezen. Als je het spel 1 miljoen keer speelt, is er een kans van 90% dat het inkomen tussen 16, 432 en - 16, 432 zal zijn.

Nadat ik met de spreadsheet en het programma had gewerkt en miljoenen simulaties had uitgevoerd, was ik in staat om de defecten in het programma uit te werken, de defecten in de spreadsheet aan te pakken en waarden te definiëren voor de uitbetalingstabel die de VI <20 hield. Ten slotte veranderde ik de hold tot 15% en voerde nog een set van 5 simulaties uit om te controleren of het inkomen van het spel in overeenstemming is met de verwachtingen als het zou worden ingezet in een echte wereldsituatie. Hier is de inkomstentabel voor een 15% hold.

En hier zijn de daadwerkelijke resultaten.

Als je echt alle wiskunde achter het instellen van de uitbetalingswaarden wilt begrijpen, raad ik je aan om de formules in de spreadsheet te bekijken. Als u fouten vindt, wijs ze dan alstublieft aan mij; Ik ben geen wiskundige (of een C-programmeur) van beroep, dus de standaard disclaimer is van toepassing.

De code

Ik zal u niet regel voor regel door de code leiden. Het is uitgebreid becommentarieerd en er is nergens iets lastigs aan de hand. Dus gebruik de Force, lees de bron. Als je niet bekend bent met de manipulatie van registers op de ATmega386 en meer wilt weten over het schrijven van code voor de AVR-microcontroller zonder afhankelijk te zijn van de Arduino-bibliotheek, raad ik je aan om een ​​exemplaar van Elliott William's uitstekend boek, "Make:AVR Programming". Als je toevallig een abonnement hebt op safaribooksonline.com, dan vind je het daar. Anders is het hier beschikbaar op Amazon. In deze programma's gebruik ik op sommige plaatsen de Arduino-functies en op andere plaatsen manipuleer ik de registers rechtstreeks. Sorry daarvoor.

Het eerste dat opvalt, is dat het programma uitgebreid gebruik maakt van globale variabelen. Er is een goede discussie over dit onderwerp op Stack Overflow. Ik ga hier geen intensief gebruik van globale variabelen promoten of verdedigen, maar ik zou je willen aanmoedigen om alle perspectieven op het onderwerp te begrijpen en te erkennen dat er een sterk argument is om ze te gebruiken in een embedded applicatieproject met een enkele programmeur en beperkte middelen .

Ik maak wel gebruik van enkele bibliotheken, zonder welke dit project voor mij onmogelijk zou zijn geweest. De Timer Free Tone Library wordt gebruikt om verschillende frequenties door de passieve piëzo-luidspreker te sturen. In SlotMachine.h zul je merken dat er een hele reeks definities zijn voor muzieknoten. Je kunt dat gebruiken om elke gewenste melodie samen te stellen. Ik gebruik er maar een handvol van om een ​​deel van het thema van "Close Encounters of the Third Kind" te spelen wanneer de microcontroller van de SlotMachine start en de setup-functie wordt uitgevoerd. Ik heb de timer-vrije bibliotheek gekozen omdat ik dacht dat ik de timer ergens voor nodig zou hebben, maar uiteindelijk gebruikte ik de timer helemaal niet. Het is beschikbaar als je het nodig hebt. De LED Control Library wordt gebruikt in zowel SlotMachine.ino als slotCreditDisplaySlave.ino. In de eerste wordt het gebruikt om de drie 8 x 8 LED-matrices te besturen die dienen als de rollen van de gokmachines. In slotCreditDisplaySlave.ino vergemakkelijkt de bibliotheek de toegang tot het 8-cijferige display met zeven segmenten dat het tegoed van de speler weergeeft. Dit zou een goed moment zijn om te vermelden dat ik heb geprobeerd om het gebruik van een andere AVR-chip (ATmega328) te vermijden, alleen maar om het tegoed op te dienen, maar ik kon geen manier vinden om de 8 x 8-matrices en het 8-cijferige zevensegmentendisplay van een enkele microcontroller. Dus uiteindelijk moest ik een I2C-slaaf maken om dat doel te dienen. Het is zeker zo dat je een goedkopere AVR zou kunnen gebruiken om het tegoed weer te geven, maar om het voor dit artikel eenvoudig te houden, heb ik ervoor gekozen om een ​​andere ATmega328P-PU-chip te gebruiken. Aan de positieve kant, wanneer je een grote jackpot wint, blijven de credits optellen op de credit display slave, terwijl je verder kunt gaan en opnieuw kunt draaien. De LiquidCrystal/LCD- en de LiquidCrystal I2C-bibliotheken zijn nodig om de toegang tot het 20 regels x 4 rijen LCD-scherm te vergemakkelijken. Zoals vermeld, kunt u een 20 x 2 LCD-scherm vervangen als dat alles is wat u bij de hand hebt, gewoon door de definitie van LCD_SCREEN_HEIGHT te wijzigen van 4 in 2. Zorg ervoor dat het LCD-scherm dat u voor dit project aanschaft I2C-compatibel is. Als dit niet het geval is, moet u een I2C SPI seriële interfacekaart-poortmodule voor de LCD1602-adapterplaat, onderdeelnummer PCF8574, hieronder afgebeeld, aanschaffen en deze op uw LCD1602-display solderen.

Het spel kan zich tegelijkertijd in een aantal verschillende toestanden bevinden en de variabele machineState houdt de toestanden bij. Het kan bijvoorbeeld tegelijkertijd 'spinnen' en in 'auto-modus' zijn. Ik maak niet echt intensief gebruik van dit concept in het programma; in ieder geval niet zoveel als ik in andere programma's heb. Maar er is enige voorwaardelijke vertakking op basis van de staat. Er is ook het concept van gebeurtenissen en gebeurtenissen worden verzonden en afgehandeld in de functie ProcessEvents. Het zou waarschijnlijk beter zijn als er een wachtrij voor evenementen was, maar zo ver ging ik niet.

Er is een lijst met bekende defecten en 'to do's' in het opmerkingengedeelte van SlotMachine.ino. Soms, wanneer u de rollen 'draait' (door op de draaiknop te drukken of de optie 'Spelen' in het LCD-menu te selecteren), bewegen een of zelfs twee van de rollen niet. Dat komt omdat de generator voor willekeurige getallen achter de schermen een symbool heeft gekozen dat al voor die rol wordt weergegeven. Dit kan worden verholpen om het spel realistischer te laten lijken, maar het is niet echt een defect. De rollen draaien niet van links naar rechts, zoals bij de meeste gokautomaten. Dit wordt gedaan door ontwerp, om het simpel te houden. Het zou mogelijk zijn om de rollen van links naar rechts te laten draaien door de drie willekeurige getallen die voor elke draai worden gegenereerd in oplopende volgorde te sorteren voordat de rollen daadwerkelijk draaien, en ik stoorde me er niet aan.

Wat 'todos' betreft, zou ik op een gegeven moment bruiningsbescherming en waakhondbescherming willen toevoegen, gewoon om de oefening door te nemen en te leren hoe het moet. Merk op dat 80% van de ruimte die is toegewezen voor globale variabelen al is verbruikt. Dit is het punt waarop dingen onstabiel kunnen worden met de ATmega386- en Arduino-programma's. We zijn op dat punt met dit programma. Ik moest wat budgetteren om de boel draaiende te houden, en ik zou niet aanraden om nog meer globals aan het programma toe te voegen. Dit zou het bijvoorbeeld moeilijk maken om meer functionaliteit toe te voegen aan het gedeelte Instellingen van het menu, omdat het menu veel globale variabele ruimte in beslag neemt. Ik heb geprobeerd het probleem van de globale variabelen op te lossen door de menu's naar het programmageheugen te verplaatsen, maar ik kon dat niet krijgen om de ruimte die door globals wordt gebruikt te verminderen, denk ik omdat de compiler hoe dan ook alle ruimte voor de menu's vooraf moet toewijzen . Er zou meer werk kunnen worden gedaan om het spel een beetje op te fleuren; Ik zou meer gebruik kunnen maken van de RGB-LED en de piëzo-zoemer, een overwinning iets meer kunnen vieren, misschien een beter geluid kunnen maken als er geld verloren gaat, maar dat laat ik over aan iedereen die ermee wil spelen.

Ik moest alle symbolen voor het spel ontwerpen. Sommige zullen je doen denken aan het klassieke arcadespel 'Space Invaders', en misschien heb ik die ergens van geleend. De rest heb ik met de hand ontworpen en sommige zien er minder professioneel uit. Ik heb deze site gebruikt om de symbolen te ontwerpen. Als je de symbolen wilt aanpassen kan dat in SlotMachine.h, en speel je er naar hartenlust mee. Het heeft geen invloed op de programmalogica. Voor de symbolen representeer ik de getallen in grondtal 2 / binair, zodat je ze kunt ontwerpen met je teksteditor.

De code is hier beschikbaar op GitHub.

De gokautomaat bouwen

Ik heb een FTDI USB-naar-serieel bord gebruikt om beide ATmega328P-PU-microcontrollers op hun plaats te programmeren. Deze verbindingen zijn niet afgebeeld in het Fritzing-schema. Volg deze link voor instructies over het instellen van het FTDI-break-outbord op uw soldeerloze breadboard. Mogelijk moet u een beetje googlen om de installatie te spijkeren. Ik geloof dat dit bericht me ook heeft geholpen bij het oplossen van een probleem dat ik had om de microcontroller automatisch te laten resetten aan het begin van het programmeren via het FTDI-breakoutboard. Vergeet niet om een ​​100 nF condensator in serie te plaatsen met de verbinding tussen de ATmega328 reset pin (positie 1/PC6/reset pin) en RTS op het FTDI break out board zodat je de resetknop niet ingedrukt hoeft te houden wanneer je wilt om de chip te programmeren. Als u ervoor kiest om uw Arduino Uno te gebruiken om de chips te programmeren, vindt u hier instructies. If you're just going to program the chips once with the supplied code it's probably quickest and easiest to just program them from the Arduino Uno.

Both mico-controllers are set up with the 'Arduino' chip (the ATmega328P-PU) on the breadboard. If you're planning on ultimately building this project by soldering the components together, or if you just want to copy what I've done here when you breadboard the project, you'll want to understand how to set up the Arduino on a breadboard. Follow the excellent instructions here for doing that. Those instructions include the procedure necessary to follow if you need to load the Arduino bootloader on the two chips, which you will most likely need to do if you purchase the chips from a supplier in China and/or via e-bay, as suggested here in the part's list. To do that you'll need an AVR programmer like the AVRISP mk II or the USBTiny ISP. You can also just use your Arduino, if you have one, to burn the bootloader. All of your options are explained when you follow the link above.

Parts

If you have some of the smaller components in your inventory already (resistors, capacitors, the crystal and the regulator) then you can get away with spending <$40 on parts for this build. If you add in the cost of the enclosure and the perfboard, it's probably approaching $60. I've tried to include the supplier I used for all of the pieces. I use AliExpress.com, Amazon.com, and ebay.com for most of my parts and tools, and all of these parts are easily sourced at any of those locations. Also, if you don't want to purchase a 20 x 4 LCD display, and you already have a 20 x 2 LCD display on hand, you can simply change LCD_SCREEN_HEIGHT in SlotMachine.ino from 4 to 2.

Here is the enclosure I've ordered, into which I'll insert the components:

This item is available here for $13.80. That's a little on the pricey side in my view. I'm hoping that everything will fit and that the top is very transparent so that I don't have to cut holes in it to see the reels and the credit balance display. We'll see how it goes when it gets here! Suggestions welcome.

Software

All of these libraries listed in the parts section will need to be installed into your Arduino development environment if you wish to compile the code so that you can upload it onto your ATmega chip. This page explains how to install an Arduino library.

Hand Tools

  • Soldering iron
  • Helping Hands

Schematic

The Fritzing schematic is available here, and the.fzz file is included with the code on GitHub.

Below I've included some directions on wiring the micro-controllers, because the Fritzing diagram is crowded. This doesn't represent all of the connections necessary, but it should clear up any confusion. I haven't grounded all of the unused pins, but I am probably going to do that in the final product. If you're having trouble following the Fritzing diagram with respect to setting up the circuitry for the power supply, remember to look here, under Adding circuitry for a power supply . Remember to add the switch between the breadboard ground rail and the power supply circuit so that you can power the circuit off and on without having to unplug or disconnect the power supply. That will be important when we put everything into an enclosure.

Slot Machine

  • Pin 1 - RTS on the FTDI USB to Serial break out board, reset button
  • Pin 2 - TXD on the FTDI USB to Serial break out board
  • Pin 3 - RXD on the FTDI USB to Serial break out board
  • Pin 4 - 1K ohm resistor - momentary 'spin' button
  • Pin 5 - 330 ohm resistor - RGB LED blue pin
  • Pin 6 - unused, consider grounding it
  • Pin 7 VCC - breadboard power rail, 0.1uF capacitor
  • Pin 8 GND - breadboard ground rail, 0.1uF capacitor
  • Pin 9 XTAL1 - 16MHz crystal, 22pF capacitor to breadboard ground rail
  • Pin 10 XTAL2 - 16MHz crystal, 22pF capacitor to breadboard ground rail
  • Pin 11 - unused, consider grounding it
  • Pin 12 - unused, consider grounding it
  • Pin 13 - unused, consider grounding it
  • Pin 14 - DIN on the 8x8 matrices
  • Pin 15 - 330 ohm resistor - RGB LED red pin
  • Pin 16 - 330 ohm resistor - RGB LED green pin
  • Pin 17 - piezo buzzer positive - negative piezo buzzer - breadboard ground rail
  • Pin 18 - CS on the 8x8 matrices
  • Pin 19 - CLK on the 8x8 matrices
  • Pin 20 AVCC - breadboard power rail, 0.1uF capacitor
  • Pin 21 AREF - breadboard power rail
  • Pin 22 GND - breadboard ground rail
  • Pin 23 - leave this pin floating, it's used to seed the random number generator
  • Pin 24 - 1K ohm resistor - momentary 'navigate up' button
  • Pin 25 - 1K ohm resistor - momentary 'navigate down' button
  • Pin 26 - 1K ohm resistor - momentary 'select' button
  • Pin 27 SDA - Pin 27 SDA on the display I2C ATmega328P-PU slave
  • Pin 28 SCL - Pin 28 SCL on the display I2C ATmega328P-PU slave

Display Slave

  • Pin 1 - RTS on the FTDI USB to Serial break out board, reset button
  • Pin 2 - TXD on the FTDI USB to Serial break out board
  • Pin 3 - RXD on the FTDI USB to Serial break out board
  • Pin 4 - unused, consider grounding it
  • Pin 5 - unused, consider grounding it
  • Pin 6 - unused, consider grounding it
  • Pin 7 VCC - breadboard power rail, 0.1uF capacitor
  • Pin 8 GND - breadboard ground rail, 0.1uF capacitor
  • Pin 9 XTAL1 - 16MHz crystal, 22pF capacitor to breadboard ground rail
  • Pin 10 XTAL2 - 16MHz crystal, 22pF capacitor to breadboard ground rail
  • Pin 11 - unused, consider grounding it
  • Pin 12 - unused, consider grounding it
  • Pin 13 - unused, consider grounding it
  • Pin 14 - unused, consider grounding it
  • Pin 15 - piezo buzzer positive - negative piezo buzzer - breadboard ground rail
  • Pin 16 - CS on the seven segment display
  • Pin 17 - CLK on the seven segment display
  • Pin 18 - DIN on the seven segment display
  • Pin 19 - unused, consider grounding it
  • Pin 20 AVCC - breadboard power rail, 0.1uF capacitor
  • Pin 21 AREF - breadboard power rail
  • Pin 22 GND - breadboard ground rail
  • Pin 23 - unused, consider grounding it
  • Pin 24 - unused, consider grounding it
  • Pin 25 - unused, consider grounding it
  • Pin 26 - unused, consider grounding it
  • Pin 27 SDA - Pin 27 SDA on the slot machine I2C ATmega328P-PU
  • Pin 28 SCL - Pin 28 SCL on the slot machineI2C ATmega328P-PU

Summary

This project was a lot of fun to build. The most challenging part was understanding all of the math necessary to create a payout table that works. I hope you can have fun with this project too, if you decide to build it. If you have any problems, questions, or, most importantly, discover any defects in the code or with the math, please contact me so I can fix any problems! My email address is [email protected]. I'll be creating part II of this article when I enclose all of the components.

Code

  • SlotMachine.ino
  • SlotMachine.h
  • slotCreditsDisplaySlave.ino
SlotMachine.inoArduino
/*SlotMachine.ino Version:1.0 Date:2018/07/01 - 2018/08/29 Device:ATMega328P-PU @ 16mHz Language:C Purpose =======A slot machine for entertainment and educational purposes only, with the following features:- AtMega328P microcontroller running at 16mHz - Custom I2C seven segment display for displaying credit balance, also built with an ATMega328P running at 16mHz. That program is supplied in a seperate file. - Three 8x8 LED matricies for displaying symbols driven by MAX7219. - I2C LCD display 20x4, to show menus - various buzzers, buttons and an RGB LED. - the ability to update various settings via the LCD menu to influence the machine's behavior. - the ability to change the amount of the wager. Known Defects =============- Sometimes one or two of the reels won't spin, not really a defect. - crash as soon as payed out exceeds 1,000,000. TODO ====- add brown out detection - add watch dog protection (wdt_enable(value), wdt_reset(), WDTO_1S, WDTO_250MS) Warnings ========- Beware of turning on too much debugging, it's easy to use all of the data memory, and in general this makes the microcontroller unstable. - Gambling is a tax on people who are bad at math. This is for entertainment only. It was the intent of the author to program this game to return ~%hold of every wager to the house, similar to many slot machines. - Why not control the LED that displays the credits with the LedControl library? I tried that and couldn't get more than one LedControl object to work at a time. So I had to create an I2C slave instead and use another AVR. Suggestions ===========- Best viewed in an editor w/ 160 columns, most comments are at column 80 - Please submit defects you find so I can improve the quality of the program and learn more about embedded programming. Author ======- Copyright 2018, Daniel Murphy  - Contributors:Source code has been pulled from all over the internet, it would be impossible for me to cite all contributors. Special thanks to Elliott Williams for his essential book "Make:AVR Programming", which is highly recommended. Thanks also to Cory Potter, who gave me the idea to do this. License =======Daniel J. Murphy hereby disclaims all copyright interest in this program written by Daniel J. Murphy. This program is free software:you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; zonder zelfs de impliciete garantie van VERKOOPBAARHEID of GESCHIKTHEID VOOR EEN BEPAALD DOEL. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Libraries =========- https://github.com/wayoda/LedControl - https://bitbucket.org/teckel12/arduino-timer-free-tone/wiki/Home - https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library - https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home The Program ===========- Includes */#include #include #include  // for the abs function#include "LedControl.h" // https://github.com/wayoda/LedControl#include "SlotMachine.h"#include  // https://bitbucket.org/teckel12/arduino-timer-free-tone/wiki/Home#include #include  // https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home#include  // https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library//- Payout Table/* Probabilities based on a 1 credit wager Three spaceships:1 / (25 * 25 * 25) =0.000064 Any three symbols:24 / 15625 =0.001536 Two spaceships:(24 * 3) / 15625 =0.004608 One spaceship:(24 * 24 * 3)/ 15625 =0.110592 Tw o symbols match:(23 * 3 * 24) / 15625 =0.105984 House win, 1 minus sum of all probabilities =0.777216 _ Use the spreadsheet to work out the payout table remembering to keep the volatility resonable i.e. <20. P R O O F Actual Actual Winning Combination Payout Probablility Count Probability ========================================================*/#define THREE_SPACESHIP_PAYOUT 600 // 0.000064 0.00006860 see the excel spreadsheet #define THREE_SYMBOL_PAYOUT 122 // 0.001536 0.00151760 that accompanies this program.#define TWO_SPACESHIP_PAYOUT 50 // 0.004608 0.00468740#define ONE_SPACESHIP_PAYOUT 3 // 0.110592 0.11064389#define TWO_SYMBOL_PAYOUT 2 // 0.105984 0.10575249//// With these payouts the Volatility Index is 16.43////- Macros#define ClearBit(x,y) x &=~y#define SetBit(x,y) x |=y#define ClearBitNo(x,y) x &=~_BV(y) #define SetState(x) SetBit(machineState, x)//- Defines#define DEBUG 1 // turns on (1) and off (0) output from debug* functions#define BAUD_RATE 38400 // Baud r ate for the Serial monitor #define NUMFRAMES 25 // Number of symbols in each "reel" or "slot". e.g three reels:|7|7|7|#define LINESPERFRAME 8 // each line corresponds to one row on the 8x8 dot matrix LED#define FRAME_DELAY 100 // milliseconds, controls the speed of the spinning reels#define NUMREELS 3 // the hardware (8x8 matricies) accomodates 4 reels, we're only using three now #define DEBOUNCE_TIME 1000 // microseconds (changed from 500 to 1000 to cut down on double press problem) #define BUTTON_DDR DDRD // this accomodates the button that starts the reels spinning#define BUTTON_PORT PORTD#define BUTTON_PIN PIND#define PCMSK_BUTTON PCMSK2#define PCIE_BUTTON PCIE2 #define BUTTON_SPIN_PIN DDD2 // the actual spin button#define BUTTON_SPIN_INT PCINT18#define BUTTON_SPIN_PORT PORTD2 #define NAV_DDR DDRC // this is for the buttons that control menu navigation on the 20x4 LCD#define NAV_PORT PORTC#define NAV_PIN PINC#define PCMSK_NAV PCMSK1#define PCIE_NAV PCIE1 #define NAV_UP_PIN DDC1 // Navigate up button#define NAV_UP_INT PCINT9#define NAV_UP_PORT PORTC1 #define NAV_DOWN_PIN DDC 2 // Navigate down button#define NAV_DOWN_INT PCINT10#define NAV_DOWN_PORT PORTC2 #define SELECT_PIN DDC3 // Select current menu item button#define SELECT_INT PCINT11#define SELECT_PORT PORTC3 #define BUZZER_DDR DDRB // This is for the slot machines piezo buzzer#define BUZZER_PORT PORTB#define BUZZER_PIN DDB3#define TONE_PIN 11 // Pin you have speaker/piezo connected to (TODO:be sure to include a 100ohm resistor).#define EVENT_NONE 0 // These are all of the various events that can occur in the machine#define EVENT_SPIN 1#define EVENT_SHOW_MENU 2 #define EVENT_SELECT 3#define EVENT_NAV_UP 4#define EVENT_NAV_DOWN 5#define EVENT_BACK 6#define EVENT_PLAY 10#define EVENT_BET 11#define EVENT_SETTINGS 12#define EVENT_VIEW_METRICS 13#define EVENT_RESET 14#define EVENT_HOLD 15#define STATE_IDLE B00000001 // These are the various states the machine can be in, not all are#define STATE_SPINNING B00000010 // mutually exclusive.#define STATE_AUTO B00000100 // This state is for automatically running the program to gather metrics.#define STATE_SHOW_MENU B00001000 // State we're in when showing the menu. Note you can spin and show menu // concurrently.#define MINIMUM_WAGER 5 // TODO:consider this something that can be changed via settings#define WAGER_INCREMENT 5 // TODO:consider this something that can be changed via settings#define ONE_SECOND 1000 // # milliseconds in one second. Used to control how long the siren sounds. #define SHIP_LOC 144 // Location of various symbols in the array of symbols maintained in SlotMachine.h#define ALIEN_1_LOC 152 // needed for animation#define ALIEN_2_LOC 160#define EEPROM_FREQ 10000 // Write to EEPROM every Nth play#define AUTO_MODE_MAX 1000000 // stop after this many plays in auto mode#define RED 1 // TODO:should we use an enum here? Must be a better way...#define GREEN 2#define BLUE 3#define PURPLE 4#define WHITE 5#define OFF 6#define MAX_NOTE 4978 // Maximum high tone in hertz. Used for siren.#define MIN_NOTE 31 // Minimum low tone in hertz. Used for siren.#define STARTING_CREDIT_BALANCE 500 // Number of credits you have at "factory reset".#define DEFAULT_HOLD 0 // default hold is zero, over time the machine pays out whatever is wagered#define NUM_LED_DATAIN 7#define NUM_LED_CLK 6#define NUM_LED_LOAD 5#define NUM_CHIP_COUNT 1#define MATRIX_LED_DATAIN 8#define MATRIX_LED_CLK 13#define MATRIX_LED_LOAD 12#define MATRIX_CHIP_COUNT 4#define LOW_INTENSITY 1 // dim#define HIGH_INTENSITY 10 // bright#define SIREN_FLASHES 1#define LCD_SCREEN_WIDTH 20#define LCD_SCREEN_HEIGHT 4#define CREDITS_I2C_SLAVE_ADDR 0x10 // I2C addresses#define LCD_I2C_ADDR 0x3F // LCD display w/ 4 lines#define BACKLIGHT_PIN 3#define En_pin 2#define Rw_pin 1#define Rs_pin 0#define D4_pin 4#define D5_pin 5#define D6_pin 6#define D7_pin 7#define MENU_SIZE 17#define MAIN_MENU_NUMBER 0#define MAIN_MENU_ELEMENTS 6char *mainMenu[] ={ "Play", "Bet", "Settings", "Metrics", "Reset", "Hold", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " };#define B ET_MENU_NUMBER 1#define BET_MENU_ELEMENTS 3char *betMenu[] ={ "+5 credits:", // TODO:make this dynamic based on WAGER_INCREMENT "-5 credits:", "Back", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " };#define SETTINGS_MENU_NUMBER 2#define SETTINGS_MENU_ELEMENTS 3#define SETTINGS_BACK_ITEM 2char *settingsMenu[] ={ "Auto/Manual", // TODO:fill out this menu with more cool options "Toggle Sound ", "Back ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " };#define METRICS_MENU_NUMBER 3#define METRICS_MENU_ELEMENTS 15char *metricsMenu[METRICS_MENU_ELEMENTS];#define HOLD_MENU_NUMBER 4#define HOLD_MENU_ELEMENTS 3char *holdMenu[] ={ "+1 percent:", "-1 percent:", "Back", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " };int selectPos =0;int menuNumber =MAIN_MENU_NUMBER;int elements =MAIN_MENU_ELEMENTS;char *currentMenu[MENU_SIZE];LiquidCrystal_I2C lcd( LCD_I2C_ADDR, // Create the LCD display object for the 20x4 display En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin );LedControl lc=LedControl( MATRIX_LED_DATAIN, // Create the LED display object for the 8x8 matrix MATRIX_LED_CLK, MATRIX_LED_LOAD, MATRIX_CHIP_COUNT ); // Pins:DIN,CLK,CS, # of chips connectedvolatile int reelArrayPos[NUMREELS];volatile byte machineState;volatile byte event =EVENT_NONE;volatile byte color =RED;#define ADC_READ_PIN 0 // we read the voltage from this floating pin to seed the random number generator#define RED_PIN 9 // Pin locations for the RGB LED#define GREEN_PIN 10#define BLUE_PIN 3#define NUM_NOTES 5 // The number of notes in the melody // EEProm address locations#define PAYEDOUT_ADDR 0x00 // 4 bytes#define WAGERED_ADDR 0x04 // 4 bytes#define PLAYED_ADDR 0x08 // 4 bytes#define TWO_MATCH_ADDR 0x12 // 4 bytes#define THREE_MATCH_ADDR 0x16 // 2 bytes#define SHIP_ONE_MATCH_ADDR 0x18 // 4 bytes#define SHIP_TWO_MATCH_ADDR 0x22 // 2 bytes#define SHIP_THREE_MATCH_ADDR 0x24 // 2 bytes#define EEPROM_WRITES_ADDR 0x34 // 4 bytes#define RESET_FLAG_ADDR 0x38 // 4 bytes#define CREDIT_BALANCE_ADDR 0x42 // 4 bytes#define HOLD_ADDR 0x46 // 2 bytesboolean sound =true;byte reelMatches =0; // per play variablesbyte shipMatches =0;unsigned long wagered =0; // amount wagered on a single spindouble owedExcess =0; // change, need to track this so hold is accurateunsigned long twoMatchCount =0; // 1 if two symbols matchunsigned int threeMatchCount =0; // 1 if three symbols matchunsigned long shipOneMatchCount =0; // 1 if there's one ship presentunsigned int shipTwoMatchCount =0; // 1 if there are two ships presentunsigned int shipThreeMatchCount =0; // 1 if there are three ships present (Jackpot!)unsigned long totalCalcs =0; // total plays only relavent in auto modesigned long startingCreditBalance; // the credit balance before spinningint increment =WAGER_INCREMENT;#define DISP_CREDIT_INCREMENT 1 // on the seven segment display, increment/decrement the balance by this value until the final value is reached. // lifetime variables (stored in EEprom) Reset sets most back to zerounsigned long storedPayedOut; // sum of all payoutsunsigned long storedWagered; // sum of all wagers (profit =payouts - wagers)unsigned long storedPlays; // the number of spinsunsigned long storedTwoMatchCount; // number of times two symbols have matchedunsigned int storedThreeMatchCount; // number of times three symbols have matchedunsigned long storedShipOneMatchCount; // number of times one ship has appearedunsigned int storedShipTwoMatchCount; // number of time two ships have appearedunsigned int storedShipThreeMatchCount; // number of times three ships have appeared (Jackpot!)unsigned long storedEEpromWrites; // number of times we've written to EEprom. 100,000 is the approximate maximumsigned long storedCreditBalance; // the credit balance.int storedHold =DEFAULT_HOLD; // the house advantage, in percent, usually between 1 and 15, 2 bytes volatile byte portdhistory =0b00000100; // default is high because of the pull-up, correct settingvolatile byte portchistory =0b00001110; // default is high because of the pull-up, correct setting //- Debugging Routines // These routines are helpful for debugging, I will leave them in for your use. // For sending output to the serial monitor. Set the baud rate in setup.void debug(String text) { if (DEBUG) { Serial.println(text); }}void debugNoLF(String text) { if (DEBUG) { Serial.print(text); }}void debugInt(signed int anInt) { if (DEBUG) { char myInt[10]; itoa(anInt,myInt,10); debug(myInt); }}void debugLong(signed long aLong) { if (DEBUG) { char myLong[10]; ltoa(aLong,myLong,10); debug(myLong); }}void debugDouble(double aDouble) { if (DEBUG) { char *myDouble =ftoa(aDouble); debug(myDouble); }}void debugMetric(const char myString[], signed int anInt) { if (DEBUG) { debugNoLF(myString);debugNoLF(F(":")); debugInt(anInt); Serial.print(F("\r\n")); }}void debugMetricLong(const char myString[], signed long aLong) { if (DEBUG) { debugNoLF(myString);debugNoLF(F(":")); debugLong(aLong); Serial.print(F("\r\n")); }}void debugStoredMetrics() { for (int i =0; i <11; i++) { debug(metricsMenu[i]); }}void debugMetricDouble(const char myString[], double aDouble) { if (DEBUG) { debugNoLF(myString);debugNoLF(F(":")); debugDouble(aDouble); Serial.print(F("\r\n")); }} // quick and dirty ftoa for legacy codechar *ftoa(double f) // from https://www.microchip.com/forums/m1020134.aspx{ static char buf[17]; char * cp =buf; unsigned long l, rem; if(f <0) { *cp++ ='-'; f =-f; } l =(unsigned long)f; f -=(double)l; rem =(unsigned long)(f * 1e6); sprintf(cp, "%lu.%10.10lu", l, rem); return buf;}//- All Other Functionsvoid beep() { // Beep and flash LED green unless STATE_AUTO setGreen(); if (sound) { BUZZER_PORT |=(1 < 0) { celebrateWin(reelMatches); } setupMetricsMenu(); } else if ((totalCalcs++%EEPROM_FREQ) ==0) { // EEPROM can be written ~100,000 times, storeMetrics(); displayCredits(); // displayCredits takes care of the sign on increment setupMetricsMenu(); debugStoredMetrics(); debugMetricDouble("owedExcess",owedExcess); // don't want to put owedExcess in metricsMenu because of global var space shortage if (totalCalcs>=AUTO_MODE_MAX) { // drop out of auto mode when threshold exceeded ClearBit(machineState, STATE_AUTO); SetState(STATE_IDLE); event =EVENT_NONE; } } ClearBit(machineState, STATE_SPINNING);}void spin() {//debug("spin()"); SetState(STATE_SPINNING); if (!(STATE_AUTO ==(machineState &STATE_AUTO))) { beep(); } zeroAllBalances(); byte reelsStopped[NUMREELS] ={0,0,0}; byte stopArrayPos[NUMREELS]; for (int reelNum =0; reelNum  0) { winnings =wagered * (THREE_SPACESHIP_PAYOUT - (THREE_SPACESHIP_PAYOUT * (storedHold/100.0))); // winnings are the amount wagered times the payout minus the hold. } else if (threeMatchCount> 0) { winnings =wagered * (THREE_SYMBOL_PAYOUT - (THREE_SYMBOL_PAYOUT * (storedHold/100.0))); } else if (shipTwoMatchCount> 0) { winnings =wagered * (TWO_SPACESHIP_PAYOUT - (TWO_SPACESHIP_PAYOUT * (storedHold/100.0))); } else if (shipOneMatchCount> 0) { winnings =wagered * (ONE_SPACESHIP_PAYOUT - (ONE_SPACESHIP_PAYOUT * (storedHold/100.0))); } else if (twoMatchCount> 0) { winnings =wagered * (TWO_SYMBOL_PAYOUT - (TWO_SYMBOL_PAYOUT * (storedHold/100.0))); } else { winnings =0; } signed long roundWinnings =(signed long) round(winnings); owedExcess +=winnings - roundWinnings; // owedExcess is the change; credits between -1 and 1. if (owedExcess>=1 || owedExcess <=-1) { // if we can pay out some excess int roundOwedExcess =(int) round(owedExcess); roundWinnings +=roundOwedExcess; // add the rounded portion to the winnings owedExcess -=roundOwedExcess; // subtract out what we added to continue to track the excess } roundWinnings -=wagered; // you pay for your bet whether you won or not! // winnings -=wagered; return roundWinnings;// return((signed long) round(winnings));}void calcStored(signed long winnings) { storedPayedOut +=winnings; storedWagered +=wagered; startingCreditBalance =storedCreditBalance; storedCreditBalance +=winnings; storedPlays +=1; // calcStored is called one time per play storedTwoMatchCount +=twoMatchCount; storedThreeMatchCount +=threeMatchCount; storedShipOneMatchCount +=shipOneMatchCount; storedShipTwoMatchCount +=shipTwoMatchCount; storedShipThreeMatchCount +=shipThreeMatchCount;}void storeMetrics() { beepAuto(); // so we know we're not hung in auto mode. updateStoredPayedOut(); updateStoredWagered(); updateStoredPlays(); updateStoredTwoMatchCount(); updateStoredThreeMatchCount(); updateStoredShipOneMatchCount(); updateStoredShipTwoMatchCount(); updateStoredShipThreeMatchCount(); storedEEpromWrites++; updateStoredEEpromWrites(); updateStoredCreditBalance(); updateStoredHold();}void displayCredits() {//debug("displayCredits()"); int xmitIncrement; if ((STATE_AUTO ==(machineState &STATE_AUTO))) { // display the credits here if we're in auto mode. xmitIncrement =abs(startingCreditBalance - storedCreditBalance); // we don't want the display slave to count up/down } else { xmitIncrement =DISP_CREDIT_INCREMENT; // set increment back to what it should be during manual play } Wire.beginTransmission(CREDITS_I2C_SLAVE_ADDR); Wire.write( startingCreditBalance &0xFF); Wire.write((startingCreditBalance &0xFF00)>> 8); Wire.write((startingCreditBalance &0xFF0000)>> 16); Wire.write((startingCreditBalance &0xFF000000)>> 24); // most sigificant byte sent last if (startingCreditBalance> storedCreditBalance) { // if the player lost, xmitIncrement *=-1; // flip the sign on increment so we count down } Wire.write( xmitIncrement &0xFF); Wire.write((xmitIncrement &0xFF00)>> 8); Wire.write( storedCreditBalance &0xFF); Wire.write((storedCreditBalance &0xFF00)>> 8); Wire.write((storedCreditBalance &0xFF0000)>> 16); Wire.write((storedCreditBalance &0xFF000000)>> 24); // most sigificant byte sent last byte error =Wire.endTransmission(); if (error==4) { debug(F("Unknown error at address")); // I've never seen this happen. } }bool allReelsStopped(byte reelsStopped[]) { byte sumStopped =0; for (int i; i  
SlotMachine.hC Header File
const byte reel[] ={ // 0 star B10011001, //0 B01011010, B00111100, B11111111, B11111111, B00111100, B01011010, B10011001, // 1 one spot on dice B00000000, // 8 B00000000, B00000000, B00011000, B00011000, B00000000, B00000000, B00000000, // 2 three bars B11111111, // 16 B11111111, B00000000, B11111111, B11111111, B00000000, B11111111, B11111111, // 3 heart B01100110, // 24 B11111111, B11111111, B11111111, B11111111, B01111110, B00111100, B00011000, // 4 two spots on dice B00000000, // 32 B01100000, B01100000, B00000000, B00000000, B00000110, B00000110, B00000000, // 5 seven B00000000, // 40 B01111110, B01111110, B00001100, B00011000, B00111000, B00111000, B00000000, // 6 dollar sign B00011000, // 48 B00111100, B01011010, B00111000, B00011100, B01011010, B00111100, B00011000, // 7 three spots on dice B00000000, B01100000, B01100000, B00011000, B00011000, B00000110, B00000110, B00000000, // 8 inverse 9 spots, hashtag # B00100100, B00100100, B11111111, B00100100, B00100100, B11111111, B00100100, B00100100, // 9 one bar B00000000, B00000000, B00000000, B11111111, B11111111, B00000000, B00000000, B00000000, // 10 four on dice B00000000, B01100110, B01100110, B00000000, B00000000, B01100110, B01100110, B00000000, // 11 inverse seven B11111111, B10000001, B10000001, B11110011, B11100111, B11000111, B11000111, B11111111, // 12 9 spots B11011011, B11011011, B00000000, B11011011, B11011011, B00000000, B11011011, B11011011, // 13 five on dice B00000000, B01100110, B01100110, B00011000, B00011000, B01100110, B01100110, B00000000, // 14 two bars B00000000, B11111111, B11111111, B00000000, B00000000, B11111111, B11111111, B00000000, // 15 Alien 0 (120) B01000010, B00100100, B01111110, B11011011, B11111111, B11111111, B10100101, B00100100, // 16 smile face (128) B00000000, B00100100, B00000000, B00011000, B01000010, B01000010, B00111100, B00011000, // 17 6 on dice (136) B00000000, B11011011, B11011011, B00000000, B00000000, B11011011, B11011011, B00000000, // 18 SpaceShip (144) B00000000, B00000000, B00111100, B01111110, B10101011, B01111110, B00111100, B00000000, // 19 Alien 1 (152) B00011000, B00111100, B01111110, B11011011, B11111111, B00100100, B01011010, B10100101, // 20 Alien 2 (160) B00011000, B00111100, B01111110, B11011011, B11111111, B00100100, B01011010, B01000010, // 21 Alien 3 (168) B00000000, B10000001, B11111111, B11011011, B11111111, B01111110, B00100100, B01000010, // 22 one B00010000, B00110000, B00010000, B00010000, B00010000, B00010000, B00010000, B00111000, // 23 two B00111000, B01000100, B10000010, B00000100, B00001000, B00010000, B00100000, B11111110, // 24 three B11111111, // 192 B00000010, B00000100, B00011100, B00000010, B00000100, B00001000, B11100000};/************************************************* * Public Constants *************************************************/#define NOTE_B0 31#define NOTE_C1 33#define NOTE_CS1 35#define NOTE_D1 37#define NOTE_DS1 39#define NOTE_E1 41#define NOTE_F1 44#define NOTE_FS1 46#define NOTE_G1 49#define NOTE_GS1 52#define NOTE_A1 55#define NOTE_AS1 58#define NOTE_B1 62#define NOTE_C2 65#define NOTE_CS2 69#define NOTE_D2 73#define NOTE_DS2 78#define NOTE_E2 82#define NOTE_F2 87#define NOTE_FS2 93#define NOTE_G2 98#define NOTE_GS2 104#define NOTE_A2 110#define NOTE_AS2 117#define NOTE_B2 123#define NOTE_C3 131#define NOTE_CS3 139#define NOTE_D3 147#define NOTE_DS3 156#define NOTE_E3 165#define NOTE_F3 175#define NOTE_FS3 185#define NOTE_G3 196#define NOTE_GS3 208#define NOTE_A3 220#define NOTE_AS3 233#define NOTE_B3 247#define NOTE_C4 262#define NOTE_CS4 277#define NOTE_D4 294#define NOTE_DS4 311#define NOTE_E4 330#define NOTE_F4 349#define NOTE_FS4 370#define NOTE_G4 392#define NOTE_GS4 415#define NOTE_A4 440#define NOTE_AS4 466#define NOTE_B4 494#define NOTE_C5 523#define NOTE_CS5 554#define NOTE_D5 587#define NOTE_DS5 622#define NOTE_E5 659#define NOTE_F5 698 #define NOTE_FS5 740#define NOTE_G5 784#define NOTE_GS5 831#define NOTE_A5 880#define NOTE_AS5 932#define NOTE_B5 988#define NOTE_C6 1047 #define NOTE_CS6 1109#define NOTE_D6 1175#define NOTE_DS6 1245#define NOTE_E6 1319#define NOTE_F6 1397 #define NOTE_FS6 1480#define NOTE_G6 1568 #define NOTE_GS6 1661#define NOTE_A6 1760 #define NOTE_AS6 1865#define NOTE_B6 1976#define NOTE_C7 2093#define NOTE_CS7 2217#define NOTE_D7 2349#define NOTE_DS7 2489#define NOTE_E7 2637#define NOTE_F7 2794#define NOTE_FS7 2960#define NOTE_G7 3136#define NOTE_GS7 3322#define NOTE_A7 3520#define NOTE_AS7 3729#define NOTE_B7 3951#define NOTE_C8 4186#define NOTE_CS8 4435#define NOTE_D8 4699#define NOTE_DS8 4978
slotCreditsDisplaySlave.inoArduino
/*slotCreditsDisplaySlave.ino Version:1.0 Date:2018/07/01 - 2018/07/29 Device:ATMega328P-PU @ 16mHz Language:C Purpose =======`The .purpose of this program is to function as an I2C slave responsible for displaying credits in a slot machine Known Defects =============- TODO ====- is 38400 an efficient baud rate for arduino running at 16mhz? - include a 100 ohm resistor with the piezo buzzer - is 100kHz the fastest setting we can accomodate w/ Wire library? Warnings ========- Suggestions ===========- Author ======- Copyright 2018, Daniel Murphy  License =======Daniel J. Murphy hereby disclaims all copyright interest in this program written by Daniel J. Murphy. This program is free software:you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; zonder zelfs de impliciete garantie van VERKOOPBAARHEID of GESCHIKTHEID VOOR EEN BEPAALD DOEL. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Libraries =========- https://github.com/wayoda/LedControl The Program ===========- Includes */#include #include "LedControl.h"#define BAUD_RATE 38400 #define CREDITS_SLAVE_ADDR 16 #define DISPLAY_DELAY 5#define DEBUG 1#define BUZZER_DDR DDRB#define BUZZER_PORT PORTB#define BUZZER_PIN DDB1#define TONE_PIN 9 // Pin you have speaker/piezo connected to (be sure to include a 100 ohm resistor).#define BEEP_LENGTH 100 // Now we need a LedControl to work with. // pin 12 is connected to the DataIn // pin 11 is connected to the CLK // pin 10 is connected to LOAD // We have only a single MAX72XX.LedControl lc=LedControl(12,11,10,1);static const int slaveAddress =CREDITS_SLAVE_ADDR; long volatile theCredits[10] ={0L,0L,0L,0L,0L,0L,0L,0L,0L,0L};signed long volatile displayedBalance =0;signed long volatile startingCreditBalance =0;signed long volatile endingCreditBalance;signed int volatile increment;boolean volatile updateDisplayFlag =false;void debug(String text) { if (DEBUG) { Serial.println(text); }}void debugNoLF(String text) { if (DEBUG) { Serial.print(text); }}void debugInt(signed int anInt) { if (DEBUG) { char myInt[10]; itoa(anInt,myInt,10); debug(myInt); }}void debugLong(signed long aLong) { if (DEBUG) { char myLong[10]; ltoa(aLong,myLong,10); debug(myLong); }}void debugMetric(const char myString[], signed int anInt) { if (DEBUG) { debugNoLF(myString);debugNoLF(":"); debugInt(anInt); Serial.print("\r\n"); }}void debugMetricLong(const char myString[], signed long aLong) { if (DEBUG) { debugNoLF(myString);debugNoLF(":"); debugLong(aLong); Serial.print("\r\n"); }}void beep() { BUZZER_PORT |=(1 <  

Schema's

slotmachine_1nXzMvYVPH.fzzThis spreadsheet was used to prove that the payout table is correct. Sheet password is "password". slotpayouttablecalc_v1_1_SfcpHOBOvf.xlsx
Close Encounters Slot Machine
link to files on Fritzing.orgSchematics on Fritzing.org The Fritzing Schematic

Productieproces

  1. EEG Machine
  2. Automaat
  3. Stemmachine
  4. Apparaat wijzigen
  5. EKG-apparaat
  6. Naaimachine
  7. CNC-machinegereedschap
  8. Onderdelen van draaibankmachine:
  9. Draaibankmachine begrijpen
  10. Onderdeel van freesmachine:
  11. Machine begrijpen