Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Máquina tragamonedas temática ATmega Alien

Componentes y suministros

Tecnología de microchip ATmega328
ATmega328P-PU, para ser precisos. $ 3.00 Uno para SlotMachine, uno para la pantalla LED de crédito I2C esclavo.
× 2
Pantalla de siete segmentos con 8 dígitos
$ 1.20 Para mostrar el saldo crediticio del jugador.
× 1
matriz de 8x8, 4 segmentos, MAX7219
$ 3.78 Para simular carretes giratorios y mostrar los símbolos. Solo se utilizan tres de los cuatro segmentos.
× 1
I2C 2004 Serial Blue Backlight LCD Module 20 X 4 2004
$ 3.00 Para mostrar el menú de opciones. Compare precios en aliexpress. ¡No pague el envío!
× 1
Breadboard (genérico)
830 puntos $ 4,00
× 2
Botones de contacto momentáneo
$ 1.00 por 50. Uno controla el giro de los carretes, tres para navegar por el menú, dos para conectar a tierra el pin 1 de las ATmegas.
× 6
Cátodo común difuso RGB
Se usa para señalar varias cosas.
× 1
LED (genérico)
Indica si se está suministrando energía a las placas.
× 1
Resistencia de 10k ohmios
4 para abrir cada uno de los botones, 2 para el pin 1 de ATmegas.
× 6
Resistencia 1k ohm
Entre cada uno de los botones y los pines de entrada ATmega.
× 4
Resistencia de 330 ohmios
Para los cables rojo, verde y azul del LED RGB.
× 3
Cristal de 16 MHz
Uno para el ATmega328P-PU de SlotMachine y otro para el ATmega328P-PU esclavo de la pantalla LED. Ambos funcionan a 16 MHz.
× 2
Interruptor deslizante
Para la fuente de alimentación.
× 1
Buzzer
Se necesitan dos, uno para el chip SlotMachine y otro para el chip esclavo de pantalla. Sería bueno modificar el circuito para que solo uno de estos sea necesario y pueda ser compartido por ambos microcontroladores.
× 2
Condensador 22 pF
× 4
Condensador de 0,10 uF
× 6
Condensador 100 nF
Esto es opcional y solo es necesario si estás usando el adaptador serial Arduino Mini USB para programar la SlotMachine chip, como yo.
× 1
Condensador 10 µF
Para ayudar a suavizar el voltaje de suministro.
× 2
Regulador lineal (7805)
Para regular el suministro de voltaje, 5V.
× 1
Cables de puente (genéricos)
Necesitarás una buena cantidad de esto. La mayor parte del tiempo lo hago yo mismo, pero también uso los cables de puente.
× 1
Una fuente de alimentación de 5v
× 1
Adaptador serial Arduino Mini USB
$ 13.20 Esto es opcional, puede usar su Arduino Uno para programar los chips ATmega 328p-pu.
× 1
Adaptador de serie FTDI USB a TTL
$ 1.66 x 2 =$ 3.32 Para programar los ATmega328P-PU en su lugar. No se muestra en el esquema.
× 1
Placa de pruebas soldable
Una placa de pruebas soldable de tamaño completo.
× 1
Placa de pruebas SparkFun soldable - Mini
× 1
Soldadura de bolsillo- 60/40 Rosin Core 0.031 "de diámetro
× 1
Caja de plástico transparente impermeable para proyectos electrónicos
$ 13.00 Este es el adjunto.
× 1

Herramientas y máquinas necesarias

Soldador (genérico)
Helping Hands

Aplicaciones y servicios en línea

Arduino IDE
Timer Free Tone Library
Biblioteca de control de LED
LiquidCrystal / Biblioteca LCD
Biblioteca LiquidCrystal I2C

Acerca de este proyecto

Máquina tragamonedas temática ATmega Alien

Este proyecto es mi implementación de una máquina tragamonedas con temática alienígena que utiliza dos microcontroladores ATmega328P-PU. Me inspiré en la máquina tragamonedas Alien Invasion de Cory Potter y quería ampliar esa idea. La máquina tragamonedas es solo para fines educativos y de entretenimiento. Hice todo lo posible para que el juego simulara una máquina tragamonedas real lo más cerca posible. El proyecto está actualmente embarcado. Se agregará un gabinete tan pronto como lleguen las piezas de China y haya tenido la oportunidad de soldar todo. El proyecto me llevó unos dos meses construirlo en mi tiempo libre. La parte más difícil de la construcción para mí fue comprender todas las matemáticas involucradas en hacer que el juego se comporte de la forma en que la industria de los casinos esperaría que se comportara una simple máquina tragamonedas después de 500 millones de simulaciones.

Cómo funciona el juego

El juego tiene tres carretes con los mismos 25 símbolos únicos que aparecen en cada carrete (no se usa una de las matrices de 8x8 en el componente con 4 matrices de 8x8). Hay cinco formas diferentes de ganar. Si obtiene tres naves espaciales, gana el premio mayor. Si obtienes una o dos naves espaciales, también ganas algunos créditos. Si obtienes dos o tres símbolos para que coincidan, también ganas. Si obtiene una nave espacial y dos símbolos coincidentes, como se muestra a continuación, el juego se paga según el evento ganador con la menor probabilidad / el mayor pago; en otras palabras, los eventos ganadores son mutuamente excluyentes, no puedes ganar de dos formas diferentes en un solo giro de los carretes. Esto mantuvo la programación un poco más simple. Había muchos otros desafíos para mí.

Características

La máquina tragamonedas tiene varias características interesantes a las que se accede a través de la pantalla LCD con capacidad I2C de 20 x 4 utilizando dos botones de navegación y un botón de selección. Los botones utilizan un algoritmo de eliminación de rebotes bastante sofisticado que aprovecha la capacidad de interrupción externa del microcontrolador. Este es el menú principal.

Dado que hay seis líneas en el menú, debe desplazarse hacia abajo con el botón 'navegar hacia abajo' para ver el menú completo. Hay un botón dedicado a "hacer girar" los carretes. Además de eso, también puede seleccionar 'Reproducir' en el menú principal. Puede cambiar su apuesta en cualquier momento.

La característica más interesante es que el juego se puede jugar en modo "automático"; es decir, seleccionas la opción de modo automático en el menú de configuración de la pantalla LCD y el juego se reproduce una y otra vez hasta que vuelves a seleccionar la opción o se han producido 1 millón de jugadas. Esta es una función crítica para probar el juego. También puede desactivar el sonido aquí.

A través del menú en la pantalla LCD también es posible ver todas las métricas generadas a partir de la simulación. Estas métricas también se emiten y se pueden ver en el monitor en serie si conecta su microcontrolador al monitor a través de los pines RX y TX mediante un cable USB. La lista de métricas mostradas incluye su saldo de crédito, la cantidad de veces que ganó el premio mayor y la cantidad de veces que ganó créditos por cualquier otro medio. Esto me permitió ejecutar simulaciones basadas en los distintos pagos y fue útil para establecer y probar la tabla de pagos. La tabla de pagos en sí no es configurable; una vez configurado, debe permanecer igual. Supongo que sería posible configurar el índice de volatilidad usándolo para controlar las tablas de pagos, pero eso requeriría mucho más trabajo.

La opción Restablecer le permite restablecer todas las métricas (excepto las escrituras EEprom), de nuevo a cero. El chip funcionará durante aproximadamente 100 000 escrituras en EEprom. Dado que hay 512k de EEprom disponibles en el chip, y solo estamos usando una fracción de eso, sería posible mover la ubicación de las métricas en EEprom a medida que nos acercamos a 100, 000 escrituras. No he implementado esta función, pero sería un medio para extender la vida útil del chip.

Finalmente, la retención, o el porcentaje de cada apuesta que conserva la casa (a lo largo del tiempo), es configurable. Recuerde que después de realizar una operación de reinicio, la retención debe configurarse nuevamente.

El saldo acreedor del jugador siempre se muestra en una pantalla de siete segmentos de ocho dígitos.

Las matemáticas

Se trabajó mucho para asegurarnos de que el juego fuera realista. Se calcularon las probabilidades y se diseñó la tabla de pagos para que el juego tenga un Índice de Volatilidad (VI) aceptable. Este índice mide qué tan predecible es el comportamiento de la máquina. Es más probable que una máquina con un VI más alto genere más dinero para el jugador (o la casa). Es menos predecible que una máquina con un VI más bajo. Es cierto que existirá exactamente el mismo juego en diferentes casinos (o incluso en el mismo casino) con diferentes VI. El VI se cambia manipulando el calendario de pagos. Para nuestro juego, aquí están las probabilidades y los pagos para cada tipo de ganancia.

Tenga en cuenta que las probabilidades (extremo derecho) y el pago (extremo izquierdo) son dramáticamente diferentes. Si este juego se programara de modo que la tabla de pagos coincidiera o siguiera de cerca las probabilidades, su VI sería inaceptablemente alto. La retención se calcula como un porcentaje del pago y es la parte de una apuesta que mantiene la casa / casino. Como se indicó, puede establecer la retención a través del menú LCD. Tenga en cuenta que las diferentes jurisdicciones tienen diferentes regulaciones que rigen la retención máxima para las máquinas tragamonedas en esa jurisdicción. Una retención máxima típica es del 15%. Comprenda que establecer la retención al máximo permitido por la ley no necesariamente maximiza la ganancia generada por esa máquina, porque una retención más alta podría desalentar a los jugadores de usar la máquina. Sospecho, sin embargo, que muchos jugadores ignoran la retención, que generalmente está enterrada en letra pequeña, y que la curva de demanda de una máquina es relativamente vertical (lo que significa que el costo de usar la máquina, la retención, se ignora en gran medida), y ese beneficio generado por la máquina depende mucho más de la ubicación o ubicación de la máquina, así como del diseño del juego en sí. Pero eso es solo especulación. Estoy seguro de que hay algunos apostadores inteligentes que son sensibles al agarre.

La hoja de cálculo, disponible con el código, con tres tablas fue construida para demostrar que el juego está funcionando correctamente (la primera tabla aparece arriba). El primer paso para crear la hoja de cálculo fue calcular con precisión las probabilidades de cada tipo de ganancia (las columnas de Probabilidad calculada).

Tres naves espaciales

La probabilidad de que aparezcan tres naves espaciales es la inversa del número total de combinaciones posibles. El número de combinaciones ganadoras, uno, sobre el número total de combinaciones posibles, 15625. Hay 25 símbolos únicos en cada carrete, por lo que la probabilidad es 1 / (25 x 25 x 25), o 0,000064. Eso hace que las probabilidades, 1 / probabilidad - 1, sean iguales a 1 a 15624. Aquí aprendí a calcular las probabilidades a partir de la probabilidad.

Coincidencia de tres símbolos (excepto naves espaciales)

La probabilidad de que tres símbolos, además de las naves espaciales, coincidan es 24 (el número de símbolos únicos en cada carrete menos las naves espaciales) dividido por el número de combinaciones posibles. 24 es el numerador porque hay 24 combinaciones de tres símbolos coincidentes. 24/15625 =0,001536. Eso hace que las probabilidades estén entre 1 y 650,04.

Dos naves espaciales

Hay un total de 24 x 3 combinaciones de dos naves espaciales que coinciden. Eso es porque hay tres formas de hacer dos combinaciones de una nave espacial. Da X =nave espacial e Y =cualquier otro símbolo, XXY, XYX e YXX. Hay 24 valores posibles para Y. Entonces, 24 X 3/15625 =0.004608. Las probabilidades son de 1 a 216,01.

Aparece una nave espacial

Para cada carrete hay 24 x 24 combinaciones posibles para que aparezca una sola nave espacial.

Una nave espacial puede aparecer en cualquier carrete, por lo que debe multiplicar el número de combinaciones disponibles en un solo carrete por tres carretes. Entonces la probabilidad es 24 x 24 x 3/15625 =0.110592. Las probabilidades son de 1 a 8.04.

Coincidencia de dos símbolos

Para dos símbolos dados, excepto las naves espaciales, hay 23 (25 menos una nave espacial menos un símbolo que lo convertiría en una coincidencia de tres símbolos) x 3 carretes x 24 símbolos que no son naves espaciales. La probabilidad es (23 X 3 X 24) / 15625 =0.105984. Las probabilidades son de 1 a 8,44.

Ahora que tengo las probabilidades para cada tipo de ganancia, puedo usar la hoja de cálculo para diseñar la tabla de pagos de manera que el índice de volatilidad sea aceptable (<~ 20). Para entender cómo hacer esto, confié mucho en esta publicación. Ingresé valores en la columna de Ingresos de la casa de la primera tabla, usando un proceso de prueba y error, hasta que el VI estuvo por debajo de 20 y el Total en la celda J10 estuvo lo más cerca de cero que pude obtener. Usando esos valores, configuré THREE_SPACESHIP_PAYOUT, THREE_SYMBOL_PAYOUT, TWO_SPACESHIP_PAYOUT, ONE_SPACESHIP_PAYOUT y TWO_SYMBOL_PAYOUT en SlotMachine.ino en consecuencia. Luego, primero usando una retención de cero por ciento, ejecuté cinco simulaciones de 1, 000, 001 jugadas e ingresé los valores del menú de métricas en las filas y columnas apropiadas en la tabla Resultados reales (la tercera tabla).

Observé que las probabilidades reales seguían de cerca con las probabilidades calculadas y que la columna Pct Diff Prob era razonable. También hice coincidir los valores en la fila Pagos de la casa con el rango de valores de las columnas Ingresos altos e Ingresos bajos de la fila 1, 000, 000 de la tabla Comprensión del ingreso potencial (la segunda tabla), y observé que los valores de la tabla de Resultados reales estaba dentro del rango especificado por las columnas Ingresos altos e Ingresos bajos. La tabla Comprensión de los ingresos potenciales define el rango esperado de ingresos para un valor de retención dado con un intervalo de confianza del 90%. En el siguiente ejemplo, la retención se establece en 0, por lo que la probabilidad de ganar coincide con la probabilidad de perder. Si juega el juego 1 millón de veces, hay un 90% de probabilidad de que los Ingresos estén entre 16, 432 y - 16, 432.

Después de trabajar con la hoja de cálculo y el programa y ejecutar millones de simulaciones, pude resolver los defectos del programa, abordar los defectos en la hoja de cálculo y definir valores para la tabla de pagos que mantenían el VI <20. Finalmente cambié la retención al 15% y ejecutó otro conjunto de 5 simulaciones para verificar que los ingresos del juego estén en línea con las expectativas si se implementara en una situación del mundo real. Aquí está la tabla de ingresos para una retención del 15%.

Y aquí están los resultados reales.

Si realmente quiere comprender todas las matemáticas detrás de la configuración de los valores de pago, le animo a que examine las fórmulas en la hoja de cálculo. Si encuentra algún error, por favor indíquemelo; No soy matemático (ni programador en C) de profesión, por lo que se aplica el descargo de responsabilidad estándar.

El código

No lo llevaré a través del código línea por línea. Se comenta extensamente y no hay nada complicado en ninguna parte. Así que usa la Fuerza, lee la fuente. Si no está familiarizado con la manipulación de registros en el ATmega386 y le gustaría saber más sobre cómo escribir código para el microcontrolador AVR sin depender de la biblioteca Arduino, le animo a obtener una copia de Elliott William's excelente libro, "Make:AVR Programming". Si tiene una suscripción a safaribooksonline.com, la encontrará allí. De lo contrario, está disponible aquí en Amazon. En estos programas uso las funciones de Arduino en algunos lugares, y en otros lugares manipulo los registros directamente. Lo siento.

Lo primero que puede notar es que el programa hace un uso extensivo de variables globales. Hay una buena discusión sobre este tema en Stack Overflow. No voy a promover ni defender el uso intensivo de variables globales aquí, pero lo animo a que comprenda todas las perspectivas sobre el tema y reconozca que hay un fuerte argumento para usarlas en un proyecto de aplicación integrado con un solo programador y recursos limitados. .

Hago uso de algunas bibliotecas, sin las cuales este proyecto me hubiera resultado imposible. La biblioteca de tonos Timer Free se utiliza para impulsar varias frecuencias a través del altavoz piezoeléctrico pasivo. En SlotMachine.h notarás que hay una gran cantidad de definiciones para notas musicales. Puedes usar eso para armar cualquier melodía que desees. Solo uso un puñado de ellos para reproducir parte del tema de "Encuentros cercanos del tercer tipo" cuando se inicia el microcontrolador de SlotMachine y se ejecuta la función de configuración. Seleccioné la biblioteca gratuita del temporizador porque pensé que iba a necesitar el temporizador para algo, pero terminé sin usar el temporizador en absoluto. Está disponible si lo necesita. La biblioteca de control de LED se utiliza tanto en SlotMachine.ino como en slotCreditDisplaySlave.ino. En el primero se utiliza para controlar las tres matrices LED de 8 x 8 que sirven como carretes de las máquinas tragamonedas. En slotCreditDisplaySlave.ino, la biblioteca facilita el acceso a la pantalla de siete segmentos de 8 dígitos que muestra el saldo crediticio del jugador. Este sería un buen momento para mencionar que traté de evitar usar otro chip AVR (ATmega328) solo para servir el saldo de crédito, pero no pude encontrar una manera de controlar las matrices de 8 x 8 y la pantalla de siete segmentos de 8 dígitos de un solo microcontrolador. Así que al final tuve que crear un esclavo I2C para cumplir ese propósito. Definitivamente es el caso de que podría usar un AVR menos costoso para hacer el trabajo de mostrar el saldo de crédito, pero para simplificar las cosas en este artículo, elegí usar otro chip ATmega328P-PU. En el lado positivo, cuando gana un gran premio mayor, los créditos continúan contando en el esclavo de visualización de crédito mientras puede continuar y girar nuevamente. Las bibliotecas LiquidCrystal / LCD y LiquidCrystal I2C son necesarias para facilitar el acceso a la pantalla LCD de 20 líneas x 4 filas. Como se mencionó, puede sustituir una pantalla LCD de 20 x 2 si eso es todo lo que tiene a mano, simplemente cambiando la definición de LCD_SCREEN_HEIGHT de 4 a 2. Asegúrese de que la pantalla LCD que adquiera para este proyecto sea compatible con I2C. Si no es así, deberá adquirir un módulo de puerto de placa de interfaz serial I2C SPI para la placa adaptadora LCD1602, número de pieza PCF8574, que se muestra a continuación, y soldarlo a su pantalla LCD1602.

El juego puede estar en varios estados diferentes al mismo tiempo, y la variable machineState rastrea los estados. Por ejemplo, puede estar "girando" y en "modo automático" al mismo tiempo. Realmente no hago un uso intensivo de este concepto dentro del programa; no tanto como en otros programas, de todos modos. Pero hay algunas ramificaciones condicionales basadas en el estado. También existe el concepto de eventos, y los eventos se envían y manejan en la función ProcessEvents. Probablemente sería mejor si hubiera una cola de eventos, pero no fui tan lejos.

Hay una lista de defectos conocidos y "tareas pendientes" en la sección de comentarios de SlotMachine.ino. A veces, cuando 'gira' los carretes (presionando el botón de giro o seleccionando la opción 'Jugar' en el menú LCD) uno o incluso dos de los carretes no se mueven. Eso es porque el generador de números aleatorios detrás de escena eligió el símbolo que ya se muestra para ese carrete. Esto podría arreglarse para que el juego parezca más realista, pero en realidad no es un defecto. Los carretes no terminan de girar de izquierda a derecha, como sucede en la mayoría de las máquinas tragamonedas. Esto se hace por diseño, para simplificar las cosas. Sería posible que los carretes terminaran de girar de izquierda a derecha clasificando los tres números aleatorios que se generan para cada giro en orden ascendente antes de que los carretes realmente giren, y no me molesté.

En cuanto a 'todos', en algún momento me gustaría agregar protección contra oscurecimiento y protección para perros guardianes, solo para realizar el ejercicio y aprender a hacerlo. Tenga en cuenta que el 80% del espacio asignado para las variables globales ya está consumido. Este es el punto en el que las cosas pueden comenzar a volverse inestables con los programas ATmega386 y Arduino. Estamos en ese punto con este programa. Tuve que hacer algunos presupuestos para mantener las cosas funcionando, y no recomendaría agregar más globales al programa. Esto dificultaría agregar más funcionalidad a la parte de Configuración del menú, por ejemplo, porque el menú consume una gran cantidad de espacio variable global. Intenté resolver el problema de la variable global moviendo los menús a la memoria del programa, pero no pude conseguir que eso redujera el espacio utilizado por los globales, creo que porque el compilador necesita preasignar todo el espacio para los menús de todos modos . Se podría hacer más trabajo para darle vida al juego; Podría hacer más uso del LED RGB y el zumbador piezoeléctrico, celebrar un poco más una victoria, tal vez hacer un mejor sonido cuando se pierde dinero, pero se lo dejo a cualquiera que quiera jugar con él.

Tuve que diseñar todos los símbolos del juego. Algunos de ellos te recordarán el clásico juego de arcade 'Space Invaders', y es posible que los haya tomado prestados de alguna parte. El resto los diseñé a mano, y algunos de ellos tienen un aspecto menos que profesional. Usé este sitio para ayudar a diseñar los símbolos. Si desea ajustar los símbolos, puede hacerlo en SlotMachine.h, y jugar con ellos al contenido de su corazón. No afectará la lógica del programa. Para los símbolos, represento los números en base 2 / binario para que puedas diseñarlos con tu editor de texto.

El código está disponible aquí en GitHub.

Construyendo la máquina tragamonedas

Usé una placa FTDI USB a serie para programar ambos microcontroladores ATmega328P-PU en el lugar. Estas conexiones no se muestran en el esquema de Fritzing. Para obtener instrucciones sobre cómo configurar la placa de ruptura FTDI en su placa de prueba sin soldadura, siga este enlace. Es posible que deba buscar en Google un poco para concretar la configuración. Creo que esta publicación también me ayudó a solucionar un problema que estaba teniendo al intentar que el microcontrolador se reiniciara automáticamente al comienzo de la programación a través de la placa de conexión FTDI. Remember to place a 100 nF capacitor in series with the connection between the ATmega328 reset pin (position 1/PC6/reset pin) and RTS on the FTDI break out board so that you don't have to hold down the reset button when you want to program the chip. 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

  • Soldering iron
  • Helping Hands

Schematic

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

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

Slot Machine

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

Display Slave

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

Summary

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

Código

  • 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; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 <  

Esquemas

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

Proceso de manufactura

  1. Máquina de EEG
  2. Máquina expendedora
  3. Máquina de votación
  4. Cambiar máquina
  5. Máquina de electrocardiograma
  6. Máquina de coser
  7. Máquina herramienta CNC
  8. Partes de la máquina de torno
  9. Comprender la máquina de torno
  10. Componente de la fresadora
  11. Comprensión de la máquina