Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

ATmega Alien-Themen-Spielautomat

Komponenten und Verbrauchsmaterialien

Mikrochip-Technologie ATmega328
ATmega328P-PU, um genau zu sein.$3.00 Eine für die SlotMachine, eine für die I2C-Guthaben-LED-Anzeige Sklave.
× 2
Sieben-Segment-Anzeige mit 8 Ziffern
$1,20 Um das Guthaben des Spielers anzuzeigen.
× 1
8x8-Matrix, 4 Segmente, MAX7219
$3,78 Um sich drehende Walzen zu simulieren und die Symbole anzuzeigen. Nur drei der vier Segmente werden verwendet.
× 1
I2C 2004 Serielle blaue Hintergrundbeleuchtung LCD-Modul 20 x 4 2004
$3.00 Zum Anzeigen des Optionsmenüs. Shop rund um aliexpress. Zahlen Sie nicht für den Versand!
× 1
Breadboard (generisch)
830 Punkt$4,00
× 2
Kurzfristige Kontaktschaltflächen
$1.00 für 50. Einer steuert das Drehen der Walzen, drei zum Navigieren im Menü, zwei zum Erden von Pin 1 der ATmegas.
× 6
RGB diffundierte gemeinsame Kathode
Wird verwendet, um verschiedene Dinge zu signalisieren.
× 1
LED (generisch)
Zeigt an, ob die Platinen mit Strom versorgt werden.
× 1
Widerstand 10k Ohm
4 um jeden der Knöpfe hochzuziehen, 2 für Pin 1 der ATmegas.
× 6
Widerstand 1k Ohm
Zwischen den einzelnen Tasten und den ATmega-Eingangspins.
× 4
Widerstand 330 Ohm
Für die roten, grünen und blauen Leitungen der RGB-LED.
× 3
16-MHz-Kristall
Eine für die ATmega328P-PU der SlotMachine und eine für die ATmega328P-PU des LED-Anzeige-Slaves. Beide laufen mit 16MHz.
× 2
Schiebeschalter
Für das Netzteil.
× 1
Summer
Zwei sind erforderlich, einer für den SlotMachine-Chip und einer für den Display-Slave-Chip. Es wäre schön, die Schaltung so zu modifizieren, dass nur eine davon erforderlich ist und von beiden Mikrocontrollern gemeinsam genutzt werden kann.
× 2
Kondensator 22 pF
× 4
0,10 uF Kondensator
× 6
Kondensator 100 nF
Dies ist optional und wird nur benötigt, wenn Sie den seriellen Arduino Mini-USB-Adapter zum Programmieren der SlotMachine verwenden Chip, wie ich.
× 1
Kondensator 10 µF
Um die Versorgungsspannung zu glätten.
× 2
Linearregler (7805)
Um die Spannungsversorgung zu regulieren, 5V.
× 1
Jumper (generisch)
Sie benötigen eine große Menge davon. Zum größten Teil mache ich meine eigenen, aber ich benutze auch die Überbrückungsdrähte.
× 1
Ein 5-V-Netzteil
× 1
Serieller Arduino Mini-USB-Adapter
$13,20 Dies ist optional, Sie können Ihr Arduino Uno verwenden, um die ATmega 328p-pu-Chips zu programmieren.
× 1
FTDI-USB-auf-TTL-Seriell-Adapter
1,66 $ x 2 =3,32 $ Zum Programmieren der ATmega328P-PUs vor Ort. Im Schaltplan nicht abgebildet.
× 1
Lötbares Steckbrett
Ein lötfähiges Steckbrett in voller Größe.
× 1
SparkFun Lötbares Breadboard - Mini
× 1
Taschenlot - 60/40 Kolophoniumkern 0,031" Durchmesser
× 1
Durchsichtiges wasserdichtes elektronisches Projektbox-Gehäuse aus Kunststoff
$13.00 Das ist das Gehäuse.
× 1

Notwendige Werkzeuge und Maschinen

Lötkolben (generisch)
Helfende Hände

Apps und Onlinedienste

Arduino-IDE
Timer Free Tone Library
LED-Steuerungsbibliothek
LiquidCrystal/LCD-Bibliothek
LiquidCrystal I2C-Bibliothek

Über dieses Projekt

ATmega Alien-Themen-Spielautomat

Dieses Projekt ist meine Implementierung eines Alien-Themen-Spielautomaten mit zwei ATmega328P-PU-Mikrocontrollern. Ich wurde von Cory Potters Alien Invasion Slot Machine inspiriert und wollte diese Idee erweitern. Der Spielautomat dient nur zu Unterhaltungs- und Bildungszwecken. Ich habe mein Bestes gegeben, um das Spiel so nah wie möglich an einen echten Spielautomaten zu simulieren. Das Projekt ist derzeit mit Brot ausgestattet. Sobald die Teile aus China eintreffen und ich Gelegenheit hatte, alles zusammenzulöten, wird ein Gehäuse hinzugefügt. Das Projekt hat in meiner Freizeit etwa zwei Monate gedauert, bis ich es gebaut habe. Der schwierigste Teil des Builds war für mich, die gesamte Mathematik zu verstehen, die dazu beiträgt, dass sich das Spiel so verhält, wie die Casinoindustrie es von einem einfachen Spielautomaten nach einer halben Milliarde Simulationen erwarten würde.

So funktioniert das Spiel

Das Spiel hat drei Walzen mit den gleichen einzigartigen 25 Symbolen, die auf jeder Walze erscheinen (eine der 8x8-Matrizen auf der Komponente mit 4 8x8-Matrizen wird nicht verwendet.) Es gibt fünf verschiedene Möglichkeiten zu gewinnen. Wenn Sie drei Raumschiffe erhalten, gewinnen Sie den Jackpot. Wenn Sie ein oder zwei Raumschiffe bekommen, gewinnen Sie auch einige Credits. Wenn Sie zwei oder drei übereinstimmende Symbole erhalten, gewinnen Sie ebenfalls. Wenn Sie ein Raumschiff und zwei übereinstimmende Symbole erhalten, wie unten dargestellt, wird das Spiel basierend auf dem Gewinnereignis mit der niedrigsten Wahrscheinlichkeit/höchsten Auszahlung ausgezahlt; mit anderen Worten, Gewinnereignisse schließen sich gegenseitig aus, Sie können nicht auf zwei verschiedene Arten bei einer einzigen Drehung der Walzen gewinnen. Dadurch wurde die Programmierung etwas einfacher. Es gab viele andere Herausforderungen für mich.

Funktionen

Der Spielautomat verfügt über mehrere interessante Funktionen, auf die über das 20 x 4 I2C-fähige LCD-Display mit zwei Navigationstasten und einer Auswahltaste zugegriffen werden kann. Die Tasten verwenden einen ziemlich ausgeklügelten Entprellalgorithmus, der die externe Interrupt-Fähigkeit des Mikrocontrollers nutzt. Dies ist das Hauptmenü.

Da das Menü aus sechs Zeilen besteht, müssen Sie mit der Schaltfläche 'nach unten navigieren' nach unten scrollen, um das gesamte Menü zu sehen. Es gibt eine Schaltfläche zum "Drehen" der Walzen. Darüber hinaus können Sie auch im Hauptmenü „Play“ auswählen. Sie können Ihre Wette jederzeit ändern.

Das aufregendste Feature ist, dass das Spiel im 'Auto'-Modus gespielt werden kann; d.h. Sie wählen die Auto-Modus-Option aus dem Einstellungsmenü auf dem LCD-Bildschirm und das Spiel läuft immer wieder, bis Sie die Option erneut auswählen oder 1 Million Spiele aufgetreten sind. Dies ist eine kritische Funktion zum Testen des Spiels. Sie können den Ton hier auch deaktivieren.

Über das Menü auf dem LCD ist es auch möglich, alle aus der Simulation generierten Metriken anzuzeigen. Diese Metriken werden auch ausgegeben und können im seriellen Monitor angezeigt werden, wenn Sie Ihren Mikrocontroller über die RX- und TX-Pins mit einem USB-Kabel an den Monitor anschließen. Die Liste der angezeigten Metriken enthält Ihr Guthaben, die Anzahl der Jackpot-Gewinne und die Anzahl der Gewinne, die Sie auf andere Weise gewonnen haben. Dies ermöglichte mir, Simulationen basierend auf den verschiedenen Auszahlungen durchzuführen und war nützlich, um die Auszahlungstabelle zu erstellen und zu beweisen. Die Auszahlungstabelle selbst ist nicht konfigurierbar; Wenn es einmal eingestellt ist, sollte es gleich bleiben. Ich nehme an, es wäre möglich, den Volatilitätsindex konfigurierbar zu machen, indem man ihn verwendet, um die Auszahlungstabellen zu steuern, aber das würde viel mehr Arbeit erfordern.

Mit der Option Zurücksetzen können Sie alle Metriken (außer EEProm-Schreibvorgänge) auf Null zurücksetzen. Der Chip wird für etwa 100.000 Schreibvorgänge auf EEPROM funktionieren. Da auf dem Chip 512k EEprom verfügbar sind und wir nur einen Bruchteil davon verwenden, wäre es möglich, die Position der Metriken im EEprom tatsächlich zu verschieben, wenn wir uns 100.000 Schreibvorgängen nähern. Ich habe diese Funktion nicht implementiert, aber es wäre ein Mittel, um die Lebensdauer des Chips zu verlängern.

Schließlich ist der Hold oder der Prozentsatz jeder Wette, der vom Haus gehalten wird (im Laufe der Zeit), konfigurierbar. Denken Sie daran, dass nach einem Reset-Vorgang die Haltefunktion erneut eingestellt werden muss.

Das Guthaben des Spielers wird immer in einer achtstelligen Sieben-Segment-Anzeige angezeigt.

Die Mathematik

Es wurde viel Arbeit investiert, um sicherzustellen, dass das Spiel realistisch ist. Die Wahrscheinlichkeiten wurden berechnet und die Auszahlungstabelle wurde so gestaltet, dass das Spiel einen akzeptablen Volatilitätsindex (VI) hat. Dieser Index misst, wie vorhersehbar das Verhalten der Maschine ist. Eine Maschine mit einem höheren VI bringt dem Spieler (oder dem Haus) eher mehr Geld. Es ist weniger vorhersehbar als eine Maschine mit einem niedrigeren VI. Es ist wahr, dass es in verschiedenen Casinos (oder sogar im selben Casino) mit unterschiedlichen VIs genau das gleiche Spiel gibt. Das VI wird durch Manipulation des Auszahlungsplans geändert. Für unser Spiel sind hier die Wahrscheinlichkeiten und Auszahlungen für jede Art von Gewinn.

Beachten Sie, dass sich die Quoten (ganz rechts) und die Auszahlung (ganz links) dramatisch unterscheiden. Wenn dieses Spiel so programmiert wäre, dass die Auszahlungstabelle mit den Quoten übereinstimmt oder genau darauf folgt, wäre sein VI inakzeptabel hoch. Der Hold wird als Prozentsatz der Auszahlung berechnet und ist der Teil eines Einsatzes, der vom Haus/Casino gehalten wird. Wie bereits erwähnt, können Sie den Hold über das LCD-Menü einstellen. Denken Sie daran, dass verschiedene Gerichtsbarkeiten unterschiedliche Vorschriften haben, die den maximalen Hold für Spielautomaten in dieser Gerichtsbarkeit regeln. Ein typischer maximaler Halt beträgt 15 %. Verstehen Sie, dass das Setzen des Holds auf das gesetzlich zulässige Maximum nicht unbedingt den von diesem Automaten generierten Gewinn maximiert, da ein höherer Hold Spieler davon abhalten könnte, den Automaten zu benutzen. Ich vermute jedoch, dass viele Spieler den Laderaum ignorieren, der normalerweise im Kleingedruckten verborgen ist, und dass die Nachfragekurve für einen Automaten relativ vertikal verläuft (was bedeutet, dass die Kosten für die Nutzung des Automaten, des Laderaums, weitgehend ignoriert werden) und dass der vom Automaten erzielte Gewinn viel stärker vom Standort oder der Platzierung des Automaten sowie vom Design des Spiels selbst abhängt. Aber das ist nur Spekulation. Ich bin sicher, es gibt einige versierte Spieler da draußen, die empfindlich auf den Hold reagieren.

Die mit dem Code verfügbare Tabelle mit drei Tabellen wurde erstellt, um zu beweisen, dass das Spiel korrekt funktioniert (die erste Tabelle erscheint oben). Der erste Schritt beim Erstellen der Tabelle war die genaue Berechnung der Gewinnchancen für jede Art von Gewinn (die Spalten für die berechnete Wahrscheinlichkeit).

Drei Raumschiffe

Die Wahrscheinlichkeit, dass drei Raumschiffe erscheinen, ist der Kehrwert der Gesamtzahl der möglichen Kombinationen. Die Anzahl der Gewinnkombinationen, eins, über die Gesamtzahl der möglichen Kombinationen, 15625. Es gibt 25 einzigartige Symbole auf jeder Walze, daher beträgt die Wahrscheinlichkeit 1 / (25 x 25 x 25) oder 0,000064. Das macht die Quoten, 1/Wahrscheinlichkeit - 1, gleich 1 bis 15624. Ich habe hier gelernt, wie man die Quoten aus der Wahrscheinlichkeit berechnet.

Drei-Symbole-Match (außer Raumschiffe)

Die Wahrscheinlichkeit, dass drei andere Symbole als die Raumschiffe übereinstimmen, beträgt 24 (die Anzahl der einzigartigen Symbole auf jeder Walze abzüglich der Raumschiffe) geteilt durch die Anzahl der möglichen Kombinationen. 24 ist der Zähler, da es 24 Kombinationen von drei übereinstimmenden Symbolen gibt. 24/15625 =0,001536. Das macht die Quoten ungefähr 1 zu 650,04.

Zwei Raumschiffe

Es gibt 24 x 3 Gesamtkombinationen aus zwei passenden Raumschiffen. Das liegt daran, dass es drei Möglichkeiten gibt, aus einem Raumschiff zwei Übereinstimmungen zu machen. Geben Sie X =Raumschiff und Y =jedes andere Symbol, XXY, XYX und YXX. Es gibt 24 mögliche Werte für Y. Also 24 X 3 / 15625 =0,004608. Die Quoten sind 1 bis 216,01.

Ein Raumschiff erscheint

Für jede Rolle sind 24 x 24 Kombinationen für ein einzelnes erscheinendes Raumschiff möglich.

Ein Raumschiff kann auf jeder Walze erscheinen, daher müssen Sie die Anzahl der auf einer einzigen Walze verfügbaren Kombinationen mit drei Walzen multiplizieren. Die Wahrscheinlichkeit ist also 24 x 24 x 3 / 15625 =0,110592. Quoten sind 1 bis 8,04.

Zwei Symbole stimmen überein

Für zwei beliebige Symbole, mit Ausnahme der Raumschiffe, gibt es 23 (25 minus ein Raumschiff minus ein Symbol, was eine Übereinstimmung mit drei Symbolen ergeben würde) x 3 Walzen x 24 Symbole, die keine Raumschiffe sind. Die Wahrscheinlichkeit ist (23 x 3 x 24)/15625 =0,105984. Quoten sind 1 bis 8,44.

Jetzt, da ich die Wahrscheinlichkeiten für jede Art von Gewinn habe, kann ich die Tabelle verwenden, um die Auszahlungstabelle so zu gestalten, dass der Volatilitätsindex akzeptabel ist (<~20). Um zu verstehen, wie das geht, habe ich mich stark auf diesen Beitrag verlassen. Ich habe in der Spalte Hauseinkommen der ersten Tabelle nach einem Versuch und Irrtum Werte eingegeben, bis das VI unter 20 lag und die Summe in Zelle J10 so nah wie möglich an Null war. Mit diesen Werten habe ich THREE_SPACESHIP_PAYOUT, THREE_SYMBOL_PAYOUT, TWO_SPACESHIP_PAYOUT, ONE_SPACESHIP_PAYOUT und TWO_SYMBOL_PAYOUT in SlotMachine.ino entsprechend eingestellt. Dann habe ich zunächst mit einem Haltewert von null Prozent fünf Simulationen von 1.000.001 Wiedergaben durchgeführt und die Werte aus dem Metrikmenü in die entsprechenden Zeilen und Spalten in der Tabelle "Tatsächliche Ergebnisse" (die dritte Tabelle) eingegeben.

Ich beobachtete, dass die tatsächlichen Wahrscheinlichkeiten eng mit den berechneten Wahrscheinlichkeiten übereinstimmten und dass die Spalte Pct Diff Prob angemessen war. Ich habe auch die Werte in der Zeile "Haushaltszahlungen" mit dem Wertebereich aus den Spalten "Einkommen hoch" und "Einkommen niedrig" der Zeile 1.000.000 der Tabelle "Potenzielles Einkommen verstehen" (die zweite Tabelle) abgeglichen und festgestellt, dass die Werte aus die Tabelle "Ist-Ergebnisse" lag innerhalb des Bereichs, der durch die Spalten "Income High" und "Income Low" angegeben wurde. Die Tabelle „Potenzielles Einkommen verstehen“ definiert die erwartete Einkommensspanne für einen bestimmten Haltewert mit einem Konfidenzintervall von 90 %. Im folgenden Beispiel wird Hold auf 0 gesetzt, sodass die Gewinnwahrscheinlichkeit mit der Wahrscheinlichkeit des Verlierens übereinstimmt. Wenn Sie das Spiel 1 Million Mal spielen, besteht eine Wahrscheinlichkeit von 90%, dass das Einkommen zwischen 16.432 und - 16.432 liegt.

Nach der Arbeit mit der Tabellenkalkulation und dem Programm und Millionen von Simulationen konnte ich die Fehler im Programm herausarbeiten, die Fehler in der Tabellenkalkulation beheben und Werte für die Auszahlungstabelle definieren, die das VI <20 hielt. Endlich habe ich geändert den Hold auf 15 % und führte einen weiteren Satz von 5 Simulationen durch, um zu überprüfen, ob die Einnahmen des Spiels den Erwartungen entsprechen, wenn es in einer realen Situation umgesetzt würde. Hier ist die Einkommenstabelle für einen Hold von 15 %.

Und hier sind die tatsächlichen Ergebnisse.

Wenn Sie die ganze Mathematik hinter dem Festlegen der Auszahlungswerte wirklich verstehen möchten, empfehle ich Ihnen, die Formeln in der Tabelle zu untersuchen. Wenn Sie Fehler finden, weisen Sie mich bitte darauf hin; Ich bin kein Mathematiker (oder C-Programmierer) von Beruf, daher gilt der Standard-Haftungsausschluss.

Der Kodex

Ich werde Sie nicht Zeile für Zeile durch den Code führen. Es ist ausführlich kommentiert und es passiert nirgendwo etwas Schwieriges. Also benutze die Macht, lies die Quelle. Wenn Sie mit der Manipulation von Registern auf dem ATmega386 nicht vertraut sind und mehr darüber erfahren möchten, wie Sie Code für den AVR-Mikrocontroller schreiben, ohne sich auf die Arduino-Bibliothek zu verlassen, empfehle ich Ihnen, eine Kopie von Elliott Williams ausgezeichnetes Buch "Make:AVR Programming". Wenn Sie Safaribooksonline.com abonniert haben, finden Sie es dort. Ansonsten ist es hier bei Amazon erhältlich. In diesen Programmen verwende ich an manchen Stellen die Arduino-Funktionen, an anderen manipuliere ich die Register direkt. Tut mir leid.

Das erste, was Sie vielleicht bemerken werden, ist, dass das Programm in großem Umfang globale Variablen verwendet. Bei Stack Overflow gibt es eine gute Diskussion zu diesem Thema. Ich werde hier nicht die starke Verwendung globaler Variablen fördern oder verteidigen, aber ich möchte Sie ermutigen, alle Perspektiven zu diesem Thema zu verstehen und anzuerkennen, dass es ein starkes Argument dafür gibt, sie in einem eingebetteten Anwendungsprojekt mit einem einzigen Programmierer und begrenzten Ressourcen zu verwenden .

Ich nutze einige Bibliotheken, ohne die dieses Projekt für mich nicht möglich gewesen wäre. Die Timer Free Tone Library wird verwendet, um verschiedene Frequenzen durch den passiven Piezo-Lautsprecher zu treiben. In SlotMachine.h werden Sie feststellen, dass es eine Reihe von Definitionen für Musiknoten gibt. Damit können Sie jede beliebige Melodie zusammenstellen. Ich verwende nur eine Handvoll davon, um einen Teil des Themas von "Close Encounters of the Third Kind" zu spielen, wenn der Mikrocontroller der SlotMachine startet und die Setup-Funktion ausgeführt wird. Ich habe die Timer-freie Bibliothek ausgewählt, weil ich dachte, ich würde den Timer für etwas brauchen, aber am Ende habe ich den Timer überhaupt nicht verwendet. Es ist verfügbar, wenn Sie es brauchen. Die LED-Steuerungsbibliothek wird sowohl in SlotMachine.ino als auch in slotCreditDisplaySlave.ino verwendet. Im ersteren wird es verwendet, um die drei 8 x 8 LED-Matrizen zu steuern, die als Walzen der Spielautomaten dienen. In slotCreditDisplaySlave.ino erleichtert die Bibliothek den Zugriff auf die 8-stellige Sieben-Segment-Anzeige, die das Guthaben des Spielers anzeigt. Dies wäre ein guter Zeitpunkt zu erwähnen, dass ich versucht habe zu vermeiden, einen anderen AVR-Chip (ATmega328) zu verwenden, nur um das Guthaben aufzuladen, aber ich konnte keine Möglichkeit finden, die 8 x 8-Matrizen und die 8-stellige Siebensegmentanzeige von aus zu steuern ein einzelner Mikrocontroller. Am Ende musste ich also einen I2C-Slave erstellen, um diesen Zweck zu erfüllen. Es ist definitiv so, dass Sie für die Anzeige des Guthabens einen günstigeren AVR verwenden könnten, aber um die Dinge für diesen Artikel einfach zu halten, habe ich mich für einen anderen ATmega328P-PU-Chip entschieden. Auf der positiven Seite, wenn Sie einen großen Jackpot gewinnen, werden die Kredite auf dem Kreditanzeigesklaven weitergezählt, während Sie weitermachen und erneut drehen können. Die LiquidCrystal/LCD- und die LiquidCrystal I2C-Bibliotheken werden benötigt, um den Zugriff auf das 20 Zeilen x 4 Zeilen LCD-Display zu erleichtern. Wie bereits erwähnt, können Sie ein 20 x 2 LCD ersetzen, wenn das alles ist, was Sie zur Hand haben, indem Sie einfach die Definition von LCD_SCREEN_HEIGHT von 4 auf 2 ändern. Stellen Sie sicher, dass das LCD-Display, das Sie für dieses Projekt erwerben, I2C-fähig ist. Wenn dies nicht der Fall ist, müssen Sie ein I2C-SPI-Portmodul für die serielle Schnittstellenplatine für die LCD1602-Adapterplatte mit der Teilenummer PCF8574 (siehe unten) erwerben und an Ihr LCD1602-Display anlöten.

Das Spiel kann sich gleichzeitig in mehreren verschiedenen Zuständen befinden, und die Variable machineState verfolgt die Zustände. Es kann sich zum Beispiel gleichzeitig „drehen“ und im „Auto-Modus“ befinden. Ich verwende dieses Konzept innerhalb des Programms nicht wirklich; Jedenfalls nicht so viel wie in anderen Programmen. Es gibt jedoch eine bedingte Verzweigung basierend auf dem Zustand. Es gibt auch das Konzept von Ereignissen, und Ereignisse werden in der ProcessEvents-Funktion gesendet und behandelt. Es wäre wahrscheinlich besser, wenn es eine Event-Warteschlange gäbe, aber so weit bin ich nicht gegangen.

Im Kommentarbereich von SlotMachine.ino finden Sie eine Liste bekannter Mängel und "To Dos". Manchmal, wenn Sie die Walzen „drehen“ (indem Sie den Drehknopf drücken oder die Option „Spielen“ aus dem LCD-Menü auswählen), bewegen sich eine oder sogar zwei der Walzen nicht. Das liegt daran, dass der Zufallszahlengenerator hinter den Kulissen ein Symbol ausgewählt hat, das bereits für diese Rolle angezeigt wird. Dies könnte behoben werden, um das Spiel realistischer erscheinen zu lassen, aber es ist nicht wirklich ein Mangel. Die Walzen drehen sich nicht von links nach rechts, wie es bei den meisten Spielautomaten der Fall ist. Dies ist beabsichtigt, um die Dinge einfach zu halten. Es wäre möglich, dass sich die Walzen von links nach rechts drehen, indem Sie die drei Zufallszahlen, die für jede Drehung generiert werden, in aufsteigender Reihenfolge sortieren, bevor sich die Walzen tatsächlich drehen, und ich habe mich nicht darum gekümmert.

Was 'Todos' angeht, würde ich irgendwann gerne einen Brownout-Schutz und einen Watchdog-Schutz hinzufügen, nur um die Übung durchzumachen und zu lernen, wie es geht. Beachten Sie, dass bereits 80 % des für globale Variablen zugewiesenen Speicherplatzes belegt sind. Dies ist der Punkt, an dem die Dinge mit den ATmega386- und Arduino-Programmen instabil werden können. An diesem Punkt sind wir mit diesem Programm. Ich musste einige Budgets einplanen, um die Dinge am Laufen zu halten, und ich würde nicht empfehlen, dem Programm weitere Globals hinzuzufügen. Dies würde es beispielsweise schwierig machen, dem Abschnitt "Einstellungen" des Menüs weitere Funktionen hinzuzufügen, da das Menü viel Platz für globale Variablen beansprucht. Ich habe versucht, das globale Variablenproblem zu lösen, indem ich die Menüs in den Programmspeicher verschoben habe, aber ich konnte das nicht erreichen, um den von Globals verwendeten Speicherplatz zu reduzieren, ich denke, weil der Compiler sowieso den gesamten Speicherplatz für die Menüs vorab zuordnen muss . Es könnte mehr Arbeit geleistet werden, um das Spiel ein wenig aufzupeppen; Ich könnte die RGB-LED und den Piezo-Summer mehr nutzen, einen Gewinn etwas mehr feiern, vielleicht einen besseren Sound machen, wenn Geld verloren geht, aber das überlasse ich jedem, der damit spielen möchte.

Ich musste alle Symbole für das Spiel entwerfen. Einige von ihnen werden Sie an das klassische Arcade-Spiel 'Space Invaders' erinnern, und die habe ich mir vielleicht irgendwo ausgeliehen. Den Rest habe ich von Hand entworfen, und einige von ihnen sehen weniger als professionell aus. Ich habe diese Site verwendet, um bei der Gestaltung der Symbole zu helfen. Wenn Sie die Symbole anpassen möchten, können Sie dies in SlotMachine.h tun und nach Herzenslust damit spielen. Es hat keinen Einfluss auf die Programmlogik. Bei den Symbolen stelle ich die Zahlen in Basis 2 / Binär dar, damit Sie sie mit Ihrem Texteditor gestalten können.

Der Code ist hier auf GitHub verfügbar.

Bau des Spielautomaten

Ich habe eine FTDI-USB-zu-Seriell-Platine verwendet, um beide ATmega328P-PU-Mikrocontroller vor Ort zu programmieren. Diese Verbindungen sind im Fritzing-Schema nicht dargestellt. Anweisungen zum Einrichten der FTDI-Breakout-Platine auf Ihrem lötfreien Steckbrett finden Sie unter diesem Link. Möglicherweise müssen Sie ein wenig googeln, um das Setup zu treffen. Ich glaube, dieser Beitrag hat mir auch geholfen, ein Problem zu beheben, das ich hatte, als ich versuchte, den Mikrocontroller zu Beginn der Programmierung über das FTDI-Breakout-Board automatisch zurückzusetzen. Denken Sie daran, einen 100-nF-Kondensator in Reihe mit der Verbindung zwischen dem ATmega328-Reset-Pin (Position 1 / PC6 / Reset-Pin) und RTS auf der FTDI-Breakout-Platine zu platzieren, damit Sie die Reset-Taste nicht gedrückt halten müssen, wenn Sie möchten um den Chip zu programmieren. If you elect to use your Arduino Uno to program the chips, instructions are found here. 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

  • Lötkolben
  • 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; auch ohne die stillschweigende Garantie der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. 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; auch ohne die stillschweigende Garantie der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. 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 < 

Schaltpläne

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

Herstellungsprozess

  1. EEG-Maschine
  2. Automaten
  3. Abstimmungsgerät
  4. Maschine wechseln
  5. EKG-Maschine
  6. Nähmaschine
  7. Teile der Drehmaschine
  8. Drehmaschine verstehen
  9. Bestandteil der Fräsmaschine
  10. Maschine verstehen