Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Machine à sous ATmega sur le thème Alien

Composants et fournitures

Microchip Technology ATmega328
ATmega328P-PU, pour être précis. 3,00 $ Un pour la machine à sous, un pour l'affichage à LED de crédit I2C esclave.
× 2
Affichage à sept segments avec 8 chiffres
$1.20 Pour afficher le solde créditeur du joueur.
× 1
Matrice 8x8, 4 segments, MAX7219
$3.78 Pour simuler des moulinets et afficher les symboles. Seuls trois des quatre segments sont utilisés.
× 1
Module LCD à rétroéclairage bleu série I2C 2004 20 X 4 2004
$3.00 Pour afficher le menu des options. Faites le tour sur aliexpress. Ne payez pas pour l'expédition !
× 1
Planche à pain (générique)
830 point$4,00
× 2
Boutons de contact momentanés
$1.00 pour 50. Un contrôle la rotation des rouleaux, trois pour la navigation dans le menu, deux pour la mise à la terre de la broche 1 des ATmegas.
× 6
Cathode commune diffuse RVB
Utilisé pour signaler diverses choses.
× 1
LED (générique)
Indique si les cartes sont alimentées.
× 1
Résistance 10k ohm
4 pour tirer chacun des boutons, 2 pour la broche 1 des ATmegas.
× 6
Résistance 1k ohm
Entre chacun des boutons et les broches d'entrée ATmega.
× 4
Résistance 330 ohm
Pour les fils rouges, verts et bleus de la LED RVB.
× 3
Cristal 16 MHz
Un pour l'ATmega328P-PU de la machine à sous et un pour l'ATmega328P-PU de l'esclave d'affichage LED. Les deux fonctionnent à 16MHz.
× 2
Interrupteur à glissière
Pour l'alimentation.
× 1
Sonnerie
Deux sont nécessaires, un pour la puce SlotMachine et un pour la puce esclave d'affichage. Ce serait bien de modifier le circuit pour qu'un seul d'entre eux soit nécessaire et puisse être partagé par les deux microcontrôleurs.
× 2
Condensateur 22 pF
× 4
Condensateur 0,10 uF
× 6
Condensateur 100 nF
Ceci est facultatif et n'est nécessaire que si vous utilisez l'adaptateur série Arduino Mini USB pour programmer la machine à sous puce, comme moi.
× 1
Condensateur 10 µF
Pour aider à lisser la tension d'alimentation.
× 2
Régulateur linéaire (7805)
Pour réguler la tension d'alimentation, 5V.
× 1
Câbles de raccordement (générique)
Vous en aurez besoin d'une bonne quantité. Pour la plupart, je fabrique le mien, mais j'utilise aussi les câbles de démarrage.
× 1
Une alimentation 5v
× 1
Adaptateur série Arduino Mini USB
$13.20 Ceci est facultatif, vous pouvez utiliser votre Arduino Uno pour programmer les puces ATmega 328p-pu.
× 1
Adaptateur série FTDI USB vers TTL
$1.66 x 2 =$3.32 Pour programmer les ATmega328P-PU en place. Non représenté sur le schéma.
× 1
Planche à pain soudable
Une planche à pain de grande taille pouvant être soudée.
× 1
Planche à pain à souder SparkFun - Mini
× 1
Soudure de poche - 60/40 Rosin Core 0.031" de diamètre
× 1
Boîtier de projet électronique étanche en plastique transparent
$13.00 Ceci est le boîtier.
× 1

Outils et machines nécessaires

Fer à souder (générique)
Mains secourables

Applications et services en ligne

Arduino IDE
Timer Free Tone Library
Bibliothèque de contrôle LED
LiquidCrystal/LCD Library
LiquidCrystal I2C Library

À propos de ce projet

Machine à sous sur le thème ATmega Alien

Ce projet est ma mise en œuvre d'une machine à sous à thème extraterrestre utilisant deux micro-contrôleurs ATmega328P-PU. J'ai été inspiré par la machine à sous Alien Invasion de Cory Potter et je voulais développer cette idée. La machine à sous est uniquement à des fins de divertissement et d'éducation. J'ai fait de mon mieux pour que le jeu simule le plus possible une vraie machine à sous. Le projet est actuellement en planche à pain. Un boîtier sera ajouté dès que les pièces arriveront de Chine et j'ai eu la chance de tout souder. Le projet m'a pris environ deux mois à construire pendant mon temps libre. La partie la plus difficile de la construction pour moi a été de comprendre tous les calculs nécessaires pour faire en sorte que le jeu se comporte comme l'industrie des casinos s'attendrait à ce qu'une simple machine à sous se comporte après environ un demi-milliard de simulations.

Comment fonctionne le jeu

Le jeu a trois rouleaux avec les mêmes 25 symboles uniques apparaissant sur chaque rouleau (une des matrices 8x8 sur le composant avec 4 matrices 8x8 n'est pas utilisée.) Il y a cinq façons différentes de gagner. Si vous obtenez trois vaisseaux spatiaux, vous remportez le jackpot. Si vous obtenez un ou deux vaisseaux spatiaux, vous gagnez également des crédits. Si vous obtenez deux ou trois symboles correspondants, vous gagnez également. Si vous obtenez un vaisseau spatial et deux symboles correspondants, comme illustré ci-dessous, le jeu paie en fonction de l'événement gagnant avec la probabilité la plus faible/le paiement le plus élevé ; en d'autres termes, les événements gagnants s'excluent mutuellement, vous ne pouvez pas gagner de deux manières différentes sur un seul tour de rouleaux. Cela a gardé la programmation un peu plus simple. Il y avait plein d'autres défis pour moi.

Fonctionnalités

La machine à sous a plusieurs fonctionnalités intéressantes qui sont accessibles via l'écran LCD compatible I2C 20 x 4 à l'aide de deux boutons de navigation et d'un bouton de sélection. Les boutons utilisent un algorithme anti-rebond assez sophistiqué qui tire parti de la capacité d'interruption externe du microcontrôleur. Ceci est le menu principal.

Comme il y a six lignes dans le menu, vous devez faire défiler vers le bas à l'aide du bouton « naviguer vers le bas » pour voir l'ensemble du menu. Il y a un bouton dédié à « faire tourner » les rouleaux. En plus de cela, vous pouvez également sélectionner « Play » dans le menu principal. Vous pouvez modifier votre pari à tout moment.

La caractéristique la plus intéressante est que le jeu peut être joué en mode « auto » ; c'est-à-dire que vous sélectionnez l'option de mode automatique dans le menu des paramètres sur l'écran LCD et que le jeu se joue encore et encore jusqu'à ce que vous sélectionniez à nouveau l'option ou qu'un million de parties se soient produites. C'est une fonction critique pour tester le jeu. Vous pouvez également désactiver le son ici.

Via le menu sur l'écran LCD, il est également possible de visualiser toutes les métriques générées à partir de la simulation. Ces métriques sont également sorties et peuvent être visualisées sur le moniteur série si vous connectez votre microcontrôleur au moniteur via les broches RX et TX à l'aide d'un câble USB. La liste des mesures affichées comprend votre solde créditeur, le nombre de fois où vous avez touché le jackpot et le nombre de fois où vous avez gagné des crédits par tout autre moyen. Cela m'a permis d'exécuter des simulations basées sur les différents paiements et a été utile pour établir et prouver le tableau des paiements. La table de paiement elle-même n'est pas configurable; une fois réglé, il devrait rester le même. Je suppose qu'il serait possible de rendre l'indice de volatilité configurable en l'utilisant pour piloter les tables de paiement, mais cela nécessiterait beaucoup plus de travail.

L'option Réinitialiser vous permet de réinitialiser toutes les métriques (à l'exception des écritures EEprom) à zéro. La puce fonctionnera pour environ 100 000 écritures sur EEprom. Étant donné qu'il y a 512k d'EEprom disponibles sur la puce, et que nous n'en utilisons qu'une fraction, il serait possible de déplacer réellement l'emplacement des métriques dans EEprom alors que nous approchons des 100 000 écritures. Je n'ai pas implémenté cette fonctionnalité mais ce serait un moyen de prolonger la durée de vie de la puce.

Enfin, la retenue, ou le pourcentage de chaque pari conservé par la maison (au fil du temps), est paramétrable. N'oubliez pas qu'après avoir effectué une opération de réinitialisation, le blocage doit être à nouveau défini.

Le solde créditeur du joueur est toujours affiché dans un affichage à huit chiffres et sept segments.

Les maths

Beaucoup de travail a été fait pour s'assurer que le jeu était réaliste. Les probabilités ont été calculées et le tableau des gains a été conçu de manière à ce que le jeu ait un indice de volatilité (VI) acceptable. Cet indice mesure le degré de prévisibilité du comportement de la machine. Une machine avec un VI plus élevé est plus susceptible de faire gagner plus d'argent au joueur (ou à la maison). C'est moins prévisible qu'une machine avec un VI inférieur. Il est vrai que le même jeu exact existera dans différents casinos (ou même le même casino) avec des VIs différents. Le VI est modifié en manipulant le calendrier de paiement. Pour notre jeu, voici les probabilités et les gains pour chaque type de gain.

Notez que les cotes (à l'extrême droite) et le paiement (à l'extrême gauche) sont radicalement différentes. Si ce jeu était programmé de manière à ce que le tableau des gains corresponde ou suive de près les cotes, son VI serait inacceptablement élevé. La retenue est calculée en pourcentage du paiement et correspond à la partie d'une mise conservée par la maison/le casino. Comme indiqué, vous pouvez régler le maintien via le menu LCD. Gardez à l'esprit que différentes juridictions ont des réglementations différentes qui régissent la détention maximale des machines à sous dans cette juridiction. Une retenue maximale typique est de 15 %. Comprenez que fixer la prise au maximum autorisé par la loi ne maximise pas nécessairement le profit généré par cette machine, car une prise plus élevée pourrait décourager les joueurs d'utiliser la machine. Je soupçonne, cependant, que de nombreux joueurs ignorent la prise, qui est généralement enfouie dans les petits caractères, et que la courbe de demande pour une machine est relativement verticale (ce qui signifie que le coût d'utilisation de la machine, la prise, est largement ignoré), et que le profit généré par la machine dépend beaucoup plus de l'emplacement ou du placement de la machine ainsi que de la conception du jeu lui-même. Mais ce n'est que spéculation. Je suis sûr qu'il y a des joueurs avertis qui sont sensibles à la prise.

Le tableur, disponible avec le code, avec trois tableaux a été construit pour prouver que le jeu fonctionne correctement (le premier tableau apparaît ci-dessus). La première étape de la création de la feuille de calcul consistait à calculer avec précision les chances de chaque type de gain (les colonnes de probabilité calculée).

Trois vaisseaux spatiaux

La probabilité que trois vaisseaux spatiaux apparaissent est l'inverse du nombre total de combinaisons possibles. Le nombre de combinaisons gagnantes, une, sur le nombre total de combinaisons possibles, 15625. Il y a 25 symboles uniques sur chaque rouleau, donc la probabilité est de 1 / (25 x 25 x 25), ou 0,000064. Cela fait que les cotes, 1/probabilité - 1, sont égales à 1 à 15624. J'ai appris à calculer les cotes à partir de la probabilité ici.

Trois symboles correspondent (sauf les vaisseaux spatiaux)

La probabilité que trois symboles, autres que les vaisseaux spatiaux, correspondent est de 24 (le nombre de symboles uniques sur chaque bobine moins les vaisseaux spatiaux) divisé par le nombre de combinaisons possibles. 24 est le numérateur car il y a 24 combinaisons de trois symboles correspondants. 24/15625 =0,001536. Cela fait des chances d'environ 1 à 650,04.

Deux vaisseaux spatiaux

Il y a 24 x 3 combinaisons totales de deux vaisseaux spatiaux correspondants. C'est parce qu'il y a trois façons de faire deux correspondances d'un vaisseau spatial. Donnez X =vaisseau spatial et Y =tout autre symbole, XXY, XYX et YXX. Il y a 24 valeurs possibles pour Y. Donc 24 X 3 / 15625 =0,004608. Les cotes sont de 1 à 216,01.

Un vaisseau spatial apparaît

Pour chaque bobine, il y a 24 x 24 combinaisons possibles pour un seul vaisseau spatial apparaissant.

Un vaisseau spatial peut apparaître sur n'importe quelle bobine, vous devez donc multiplier le nombre de combinaisons disponibles sur une seule bobine par trois bobines. La probabilité est donc 24 x 24 x 3 / 15625 =0,110592. Les cotes sont de 1 à 8,04.

Correspondance de deux symboles

Pour deux symboles donnés, à l'exception des vaisseaux spatiaux, il y a 23 (25 moins un vaisseau spatial moins un symbole qui en ferait une correspondance à trois symboles) x 3 rouleaux x 24 symboles qui ne sont pas des vaisseaux spatiaux. La probabilité est (23 X 3 X 24)/15625 =0,105984. Les cotes sont de 1 à 8,44.

Maintenant que j'ai les probabilités pour chaque type de gain, je peux utiliser la feuille de calcul pour concevoir le tableau des gains de manière à rendre l'indice de volatilité acceptable (<~20). Pour comprendre comment faire cela, je me suis beaucoup appuyé sur ce post. J'ai entré des valeurs dans la colonne Revenu de la maison du premier tableau, en utilisant un processus d'essais et d'erreurs, jusqu'à ce que le VI soit inférieur à 20 et que le total dans la cellule J10 soit aussi proche de zéro que possible. En utilisant ces valeurs, j'ai défini THREE_SPACESHIP_PAYOUT, THREE_SYMBOL_PAYOUT, TWO_SPACESHIP_PAYOUT, ONE_SPACESHIP_PAYOUT et TWO_SYMBOL_PAYOUT dans SlotMachine.ino en conséquence. Ensuite, en utilisant d'abord une retenue de zéro pour cent, j'ai exécuté cinq simulations de 1 000 001 lectures et j'ai entré les valeurs du menu des mesures dans les lignes et les colonnes appropriées du tableau des résultats réels (le troisième tableau).

J'ai observé que les probabilités réelles suivaient de près les probabilités calculées et que la colonne Pct Diff Prob était raisonnable. J'ai également mis en correspondance les valeurs de la ligne House Pay avec la plage de valeurs des colonnes Income High et Income Low de la ligne 1 000 000 du tableau Comprendre le revenu potentiel (le deuxième tableau), et j'ai observé que les valeurs de le tableau des résultats réels se situait à l'intérieur de la plage spécifiée par les colonnes Revenu élevé et Revenu faible. Le tableau Comprendre le revenu potentiel définit la plage de revenu attendue pour une valeur de retenue donnée avec un intervalle de confiance de 90 %. Dans l'exemple ci-dessous, la retenue est définie sur 0, de sorte que la probabilité de gagner correspond à la probabilité de perdre. Si vous jouez au jeu 1 million de fois, il y a 90 % de probabilité que le revenu se situe entre 16 432 et - 16 432.

Après avoir travaillé avec la feuille de calcul et le programme et exécuté des millions de simulations, j'ai pu résoudre les défauts du programme, corriger les défauts de la feuille de calcul et définir des valeurs pour le tableau de paiement qui maintenaient le VI <20. Enfin, j'ai changé le blocage à 15 % et a effectué une autre série de 5 simulations pour vérifier que le revenu du jeu est conforme aux attentes s'il devait être appliqué dans une situation réelle. Voici le tableau des revenus pour un blocage de 15 %.

Et voici les résultats réels.

Si vous voulez vraiment comprendre tous les calculs qui sous-tendent la définition des valeurs de paiement, je vous encourage à examiner les formules de la feuille de calcul. Si vous trouvez des erreurs, veuillez me les signaler; Je ne suis pas un mathématicien (ou un programmeur C) de métier, donc la clause de non-responsabilité standard s'applique.

Le Code

Je ne vais pas vous expliquer le code ligne par ligne. C'est abondamment commenté et il n'y a rien de compliqué nulle part. Alors utilisez la Force, lisez la source. Si vous n'êtes pas familier avec la manipulation des registres sur l'ATmega386 et que vous souhaitez en savoir plus sur la façon d'écrire du code pour le micro-contrôleur AVR sans compter sur la bibliothèque Arduino, je vous encourage à obtenir une copie d'Elliott William's excellent livre, "Make:AVR Programming". Si vous avez un abonnement à safaribooksonline.com, vous le trouverez là-bas. Sinon, il est disponible ici sur Amazon. Dans ces programmes, j'utilise les fonctions Arduino à certains endroits et à d'autres, je manipule directement les registres. Désolé pour ça.

La première chose que vous remarquerez peut-être est que le programme fait un usage intensif des variables globales. Il y a une bonne discussion sur ce sujet sur Stack Overflow. Je ne vais pas promouvoir ou défendre une utilisation intensive des variables globales ici, mais je vous encourage à comprendre toutes les perspectives sur le sujet et à reconnaître qu'il existe un argument solide en faveur de leur utilisation sur un projet d'application embarquée avec un seul programmeur et des ressources limitées .

J'utilise certaines bibliothèques, sans lesquelles ce projet m'aurait été impossible. La bibliothèque de tonalités Timer Free est utilisée pour piloter diverses fréquences via le haut-parleur piézo passif. Dans SlotMachine.h, vous remarquerez qu'il existe une multitude de définitions pour les notes de musique. Vous pouvez l'utiliser pour composer la mélodie de votre choix. Je n'en utilise qu'une poignée pour jouer une partie du thème de "Rencontres rapprochées du troisième type" lorsque le micro-contrôleur de la machine à sous démarre et que la fonction de configuration s'exécute. J'ai sélectionné la bibliothèque gratuite de minuterie parce que je pensais que j'allais avoir besoin de la minuterie pour quelque chose, mais j'ai fini par ne pas utiliser la minuterie du tout. Il est disponible si vous en avez besoin. La bibliothèque de contrôle LED est utilisée à la fois dans SlotMachine.ino et slotCreditDisplaySlave.ino. Dans le premier, il est utilisé pour contrôler les trois matrices LED 8 x 8 qui servent de rouleaux de machines à sous. Dans slotCreditDisplaySlave.ino, la bibliothèque facilite l'accès à l'affichage à 8 chiffres et à sept segments qui affiche le solde créditeur du joueur. Ce serait le bon moment pour mentionner que j'ai essayé d'éviter d'utiliser une autre puce AVR (ATmega328) juste pour servir le solde créditeur, mais je n'ai pas trouvé de moyen de contrôler les matrices 8 x 8 et l'affichage à sept segments à 8 chiffres à partir de un seul microcontrôleur. Donc, à la fin, j'ai dû créer un esclave I2C pour servir cet objectif. Il est certain que vous pourriez utiliser un AVR moins cher pour afficher le solde créditeur, mais pour simplifier les choses pour cet article, j'ai choisi d'utiliser une autre puce ATmega328P-PU. Du côté positif, lorsque vous gagnez un gros jackpot, les crédits continuent de s'accumuler sur l'esclave d'affichage des crédits pendant que vous pouvez continuer et tourner à nouveau. Les bibliothèques LiquidCrystal/LCD et LiquidCrystal I2C sont nécessaires pour faciliter l'accès à l'écran LCD 20 lignes x 4 rangées. Comme mentionné, vous pouvez remplacer un écran LCD 20 x 2 si c'est tout ce que vous avez sous la main, en modifiant simplement la définition de LCD_SCREEN_HEIGHT de 4 à 2. Assurez-vous que l'écran LCD que vous achetez pour ce projet est compatible I2C. Si ce n'est pas le cas, vous devrez acquérir un module de port de carte d'interface série I2C SPI pour la plaque adaptatrice LCD1602, numéro de pièce PCF8574, illustré ci-dessous, et le souder à votre écran LCD1602.

Le jeu peut être dans plusieurs états différents en même temps, et la variable machineState suit les états. Par exemple, il peut être « en rotation » et en « mode automatique » en même temps. Je ne fais pas vraiment un usage intensif de ce concept à l'intérieur du programme; pas autant que j'ai dans d'autres programmes, de toute façon. Mais il existe des branchements conditionnels basés sur l'état. Il existe également le concept d'événements, et les événements sont distribués et gérés dans la fonction ProcessEvents. Ce serait probablement mieux s'il y avait une file d'attente d'événements, mais je ne suis pas allé aussi loin.

Il y a une liste des défauts connus et des « à faire » dans la section des commentaires de SlotMachine.ino. Parfois, lorsque vous « faites tourner » les rouleaux (en appuyant sur le bouton de rotation ou en sélectionnant l'option « Play » dans le menu LCD), un ou même deux des rouleaux ne bougent pas. C'est parce que le générateur de nombres aléatoires dans les coulisses a sélectionné le symbole qui s'affiche déjà pour cette bobine. Cela pourrait être corrigé pour rendre le jeu plus réaliste, mais ce n'est pas vraiment un défaut. Les rouleaux ne finissent pas de tourner de gauche à droite, comme ils le font sur la plupart des machines à sous. Ceci est fait par conception, pour garder les choses simples. Il serait possible que les rouleaux finissent de tourner de gauche à droite en triant les trois nombres aléatoires générés pour chaque tour dans l'ordre croissant avant que les rouleaux ne tournent réellement, et je n'ai pas pris la peine.

En ce qui concerne les « todos », j'aimerais à un moment donné ajouter une protection contre le brunissement et une protection contre les chiens de garde, juste pour faire l'exercice et apprendre à le faire. Notez que 80% de l'espace alloué aux variables globales est déjà consommé. C'est à ce stade que les choses peuvent commencer à devenir instables avec les programmes ATmega386 et Arduino. Nous en sommes à ce point avec ce programme. J'ai dû faire un peu de budgétisation pour que les choses fonctionnent, et je ne recommanderais pas d'ajouter plus de globals au programme. Cela rendrait difficile l'ajout de fonctionnalités supplémentaires à la partie Paramètres du menu, par exemple, car le menu consomme beaucoup d'espace variable global. J'ai essayé de résoudre le problème des variables globales en déplaçant les menus dans la mémoire du programme, mais je n'ai pas pu réduire l'espace utilisé par les globales, je pense parce que le compilateur doit de toute façon pré-allouer tout l'espace pour les menus . Plus de travail pourrait être fait pour pimenter un peu le jeu; Je pourrais utiliser davantage la LED RVB et le buzzer piézo, célébrer un peu plus une victoire, peut-être faire un meilleur son lorsque l'argent est perdu, mais je laisse cela à tous ceux qui veulent jouer avec.

J'ai dû concevoir tous les symboles du jeu. Certains d'entre eux vous rappelleront le jeu d'arcade classique 'Space Invaders', et je les ai peut-être empruntés quelque part. Le reste, j'ai conçu à la main, et certains d'entre eux sont moins que professionnels. J'ai utilisé ce site pour aider à concevoir les symboles. Si vous souhaitez ajuster les symboles, vous pouvez le faire dans SlotMachine.h et jouer avec eux à votre guise. Cela n'affectera pas la logique du programme. Pour les symboles je représente les nombres en base 2/binaire afin que vous puissiez les concevoir avec votre éditeur de texte.

Le code est disponible ici sur GitHub.

Construire la machine à sous

J'ai utilisé une carte FTDI USB vers série pour programmer les deux microcontrôleurs ATmega328P-PU en place. Ces connexions ne sont pas représentées dans le schéma de Fritzing. Pour obtenir des instructions sur la configuration de la carte de dérivation FTDI sur votre planche à pain sans soudure, suivez ce lien. Vous devrez peut-être googler un peu pour affiner la configuration. Je pense que cet article m'a également aidé à résoudre un problème que j'avais en essayant de réinitialiser automatiquement le microcontrôleur au début de la programmation via la carte de dérivation FTDI. N'oubliez pas de placer un condensateur de 100 nF en série avec la connexion entre la broche de réinitialisation ATmega328 (position 1/PC6/pin de réinitialisation) et RTS sur la carte de dérivation FTDI afin que vous n'ayez pas à maintenir le bouton de réinitialisation enfoncé lorsque vous le souhaitez. pour programmer la puce. 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.

Logiciel

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

Résumé

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; sans même la garantie implicite de QUALITÉ MARCHANDE ou D'ADAPTATION À UN USAGE PARTICULIER. 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; sans même la garantie implicite de QUALITÉ MARCHANDE ou D'ADAPTATION À UN USAGE PARTICULIER. 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 < 

Schémas

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

Processus de fabrication

  1. machine EEG
  2. Distributeur automatique
  3. Machine à voter
  4. Changer de machine
  5. Machine à coudre
  6. Pièces de machine de tour
  7. Comprendre le tour
  8. Composant de fraiseuse
  9. Comprendre la machine