Project in progress

Automated Tarot Machine © Apache-2.0

ATM is an Arduino based split flap display which aims to give users a satisfying tarot card reading experience.

  • 1,497 views
  • 1 comment
  • 12 respects

Components and supplies

Necessary tools and machines

Apps and online services

About this project

Intro

I was looking for a new project and I came across a really great write-up for an alphanumeric split flap display by Scott Bezek. I was intrigued to see how it worked, so I ordered all of the parts and got to building. In a matter of days, I had my own split flap module and I was feeling pretty proud of myself, but I wondered what else you could use a split flap display for...

After 6 months and a lot of hard work, I'm proud to present version 1.0 of the Automated Tarot Machine.

I figure now that the machine is in a place where it's fully functional, the best thing to do is to share what I've learned with anyone who might be interested in making one of these for themselves.

File Size Issue...

Some of the files for this project (mostly the card artwork) are VERY large, so I've included a link to a google drive folder that has all of the files you could possibly need to complete this project.

1: Laser Cutting / Assembling the Housing

The housing and flap wheel were first on the list of things that needed to be made for this project. In order to accomplish this, I used a piece of 16" x 24" x.25" acrylic and a laser cutter.

The housing is held together with 1 inch 8-32 bolts and nuts which makes for smooth assembly/disassembly.

The only item that requires glue is the flap wheel. I used an acrylic adhesive which is strong, and also bonds very quickly.

If you'd like to see a more detailed walkthrough of the assembly, I made a video that you can find here.

2: 3D Printing the Base

The next thing that needed to be made was the base. This holds the electronics and serves as a stand for the split flap display.

I've included the.STL files for the baseplate and cover below. Admittedly, there are a lot of problems with the current design. It gets the job done, but it's at the top of my list of things to improve when I revisit this project.

3: Cutting the Flaps / Applying the Stickers

The flaps for the display start their life as 12" x 24" x.03" sheets of acrylic which need to be laser cut into the correct flap shape. A tarot deck has 78 cards + 1 blank to display the back of a card when the unit is at it's home position. I decided to round this out to 80 with the addition of one more "top of the deck" card.

After laser cutting all of the flaps, it was time to move on to the card art. I have access to a large format printer which has the ability to both print and cut sticker material. I made files for each card which include a cut-path that matches the shape of the laser-cut acrylic flaps. I then applied the stickers to each flap in the roll.

Since each flap contains half of two different cards, the order in which the stickers are applied is VERY important. The top part of one card also contains the bottom half of the next card in the stack. (You can find the appropriate order in the Card Guide file below)

For example: If the flap has the top of the Ace of Swords on one side it will have the bottom of the Two of Swords on other side.

Once all of the stickers were applied and I had a fat stack of flaps, it was time to put them in the flap wheel. This would have to wait however, because the location of each card in the flap wheel depends on where "home" or the top of the deck should be located. To figure out how to set the home position, we'll need to move on to the next section...

4: How does it do that?! A Brief Overview

Split Flap Go Home:

The first and most important thing the split flap display needs to do is decipher where home (or in our case the top of the deck) is located. This orients the stepper motor and ensures that we know exactly where each card is located in relation to the home position so that our printouts will be accurate.

For this task I use the Hall effect sensor and a magnet that's embedded in the flap wheel.

Hall effect sensors are triggered when they sense a magnetic field, so finding home is simply a matter of rotating the flap wheel until the hall effect sensor is tripped by the magnet. The function that accomplishes this is aptly named FindHome() and it runs each time the machine is turned in addition to after every "draw" from the deck.

Because the machine goes home after each reading it's pretty stable and accurate (barring any major skips from the motor).

** Further reading on hall effect sensors

Stepper motors 101

The stepper motor that I'm using moves 1.8° per step, so 200 steps per full rotation (360° / 1.8 = 200). If you've never used stepper motors before, one really useful feature they have is the ability to be triggered in a "microstepping" mode which increases the number of steps it takes for the shaft to complete a full rotation. (this is what MS1, MS2 refers to on the easydriver)

The options for microstepping with the motor I'm using are as follows:

1/1 = 200 steps

1/2 = 400 steps

1/4 = 800 steps

1/8 = 1600 steps

** Check out the Dronebot Workshop youtube channel If you want to know anything about steppers (or using hall effect sensors as limit switches) this is a great video

** Another great stepper motor explanation video

How does it know what card is pulled?

I found that the unit functioned best when I was in 1/8 microstepping mode, so that means every time the motor takes 20 steps we will move forward one card. (1600 steps in a full rotation / 80 cards in the flap wheel = 20 steps per card).

Based on the knowledge that there should be a card every 20 steps, I assigned each card a number value which represents how many steps it takes to get from the home position to the desired card.

Pick a Card, Any Card

The PickACard() function is where the Arduino will determine which card is going to be pulled by choosing a number between 40 - 1580. (we exclude 0 - 39 because these are the home cards)

Obviously we want the card that's drawn to be as random as possible, but when I was doing the initial testing, I found that the Arduino's built in random function really wasn't doing the job.

After a little googling I found this Entropy library which uses jitter in the Arduino clock to generate it's random seed and it's been working perfectly.

Additionally, I wanted to ensure that whatever random number is pulled is a multiple of 20. The main reason for this is that I don't want there to be any variation in how far the stepper motor runs for each card. This increases the accuracy of the unit as well as making it easier for me to identify each card by a single numeric value as opposed to a range.

5: Building a Better Oracle

In a traditional tarot card reading, the direction that the cards is facing once it’s been pulled plays a role in how the card is interpreted. Generally, if the card is pulled upright it’s interpreted positively. If the card is pulled in reverse, the negative aspects of the card are highlighted.

As with everything in the realm of the esoteric and mystical, these are not hard and fast rules, but for my purposes those guidelines work just fine.

To include this element in my tarot reader, I decided that the best option would be to create multiple interpretation printouts that the Arduino could choose from once the card had been selected. Each card has 3 possible readings positive, open, and negative.

To show how the Arduino picks which interpretation to print, the example below uses the card The Fool which corresponds to number 40 in the guide.

Once the card is selected, I utilized the entropy library again to select a random number between 0 - 30. Each reading type is then assigned a range of numbers:

Postive: 0 - 9

Open: 10 - 19

Negative: 20 - 30

Depending on what number is selected, the Arduino then streams the bitmap image data for the corresponding interpretation to the thermal printer and the reading is printed out.

The bitmap image data is in a folder on the google drive called Tarot-Interpretations-MASTER; the contents of this folder need to be copied to an SD card and placed in the SD card module that's hooked up to pins 50-53.

I'm working on an in-depth video to explain this process more thoroughly so that if you wanted to modify the readings or card art you could do so. I'll edit this tutorial and post it here when it's finished.

6: Electronics and Code

The circuit diagram for this project should be pretty self explanatory, but it is by no means simple or without its pitfalls. I'm 98.154% sure the diagram is accurate, but do yourself a favor and set up the entire circuit with a breadboard first.

Below are a few things that are noteworthy to keep in mind when assembling the electronic elements and familiarizing yourself with the code.

Optional Elements:

There are two optional electronics/code elements in this build that I did not include in the circuit diagram; an external push button and LEDs.

The button is only really necessary if you want to operate the machine when it's not hooked up to the computer. You can hook it up to any pin of your choosing, but in my code it's connected to pin 11. It is coded to produce a "Card of the day" reading.

The LEDs serve an aesthetic function, but they also deepen the machine's connection to traditional tarot practices. Each tarot card in the minor arcana relates to an element (i.e. wands are fire, pentacles are earth) so I coded the LEDs to display whatever elemental color the card is associated with. The LEDs are wired to pin 7.

Thermal Printer:

Adafruit has a lot of great documentation on their printer, and one of the things I would suggest is to get your printer set up to do "Hardware handshaking." This allows the printer to receive data faster. There's a great tutorial on how to do this on the Adafruit website.

Printing Text Reversed:

Another thing you're going to have to do is make sure that the printer is capable of printing text upside down. To do this, you'll have to go into the Adafruit_Thermal.cpp file and change some code. A guide to accomplish this can be found here.

7: Putting in the flaps

Now that we've got that out of the way, it's time to revisit your fat stack of flaps. Turn on the machine and it will auto-home itself which will let you know where the top of your deck should be located.

It's worth noting here that because we have an extra "top of the deck" card, we want to be sure that the home card is the FIRST of the two blank cards. The order of the first three cards should be as follows:

Home: blank card

20 steps: blank card

40 steps: The Fool

Putting in the flaps is EASILY the most stressful part of the build. The nubs that hold the flaps in the wheel are pretty fragile, and if you're not careful you will break them.

Once the flaps are in and the code is loaded, the machine is ready to start spitting out mystical advice!

I hope you found this project as interesting and enjoyable as I did. I'm working on a video that I will post here which shows the machine going through some of its basic functions.

If you have any questions feel free to reach out, and happy building!

Code

Automated Tarot Machine CodeArduino
#include "Adafruit_Thermal.h"
#include <SD.h>
#include <SPI.h>
#include "SoftwareSerial.h"
#include <Adafruit_NeoPixel.h>
#include <Entropy.h>

//Declare pin functions on Redboard
#define stp 2
#define dir 3
#define MS1 4
#define MS2 5
#define EN  6 // stp, dir, MS1, MS2, EN are all for the Easy Driver
#define LED_PIN 7 // Optional, be sure to comment this outt if you aren't going to use LEDS
#define LED_COUNT 24
#define HOME_SENSOR 8 //This is the pin for the hall effect sensor
#define TX_PIN 9 // Arduino transmit YELLOW WIRE labeled RX on printer
#define RX_PIN 10 // Arduino receive GREEN WIRE labeled TX on printer
#define BUTTON_PIN 11 // Only relavent if you wish to operate the machine without the computer attached
#define SD_Pin 53 


Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800); // declare LEDS
SoftwareSerial mySerial(RX_PIN, TX_PIN); // Declare SoftwareSerial obj first
Adafruit_Thermal printer(&mySerial, 48);     // Pass addr to printer constructor
// Then see setup() function regarding serial & printer begin() calls.

//Declare variables for functions
char user_input;
int x;

inline void initSD() {
  pinMode(SD_Pin, OUTPUT);
  if (!SD.begin(SD_Pin)) {
    Serial.println("SD Error");
  } else {
    Serial.println("SD Ok");
  }
}

//****************************************************************************
//****************************************************************************

boolean lastButtonState = HIGH;
unsigned long currentButtonTime = 0, lastButtonTime = 0, ButtonCheckTime = 20;

//****************************************************************************
//****************************************************************************

void setup() {
  initSD(); //Initialize the SD card
  Entropy.initialize(); //Initialize the Entropy library
  pinMode(stp, OUTPUT);
  pinMode(dir, OUTPUT);
  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
  pinMode(EN, OUTPUT); //Set the pinmodes for the easydriver
  pinMode(HOME_SENSOR, INPUT); // Set the pinmode for the hall effect sensor
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  Serial.begin(9600); //Open Serial connection for debugging
  Serial.println("Begin motor control");
  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
  mySerial.begin(19200);  // Initialize SoftwareSerial
  printer.begin();        // Init printer (same regardless of serial type)

  FindHome(); // Go to the "Top of the deck" at startup
}

//****************************************************************************
//****************************************************************************

void loop(){
  
  currentButtonTime = millis();
  digitalWrite(EN, LOW); //Pull enable pin low to allow motor control

  if ( currentButtonTime - lastButtonTime > ButtonCheckTime ) {
    
    boolean buttonState = digitalRead(BUTTON_PIN);
    
    if (buttonState == LOW && lastButtonState == HIGH) {
      
      PickACard();
      CardOfTheDay();
      colorWipe(strip.Color(0, 0, 0, 0), 0); // OFF
      FindHome();
      
  } 
  lastButtonState = buttonState;
 }
  
  while(Serial.available()){
      user_input = Serial.read(); //Read user input and trigger appropriate function
      digitalWrite(EN, LOW); //Pull enable pin low to allow motor control
      if (user_input == '1'){
          Serial.println();
          Serial.print("Your card of the day is... ");
          PickACard();
          CardOfTheDay();
          colorWipe(strip.Color(0, 0, 0, 0), 0); // OFF
          FindHome();
          }
      else if (user_input == '2'){
          Serial.println();
          Serial.print("Your past was... ");
          PickACard();
          Past();
          colorWipe(strip.Color(0, 0, 0, 0), 0); // OFF
          Reset();
          Serial.print("Your present is... ");
          PickACard();
          Present();
          colorWipe(strip.Color(0, 0, 0, 0), 0); // OFF
          Reset();
          Serial.print("Your future will be... ");
          PickACard();
          Future();
          colorWipe(strip.Color(0, 0, 0, 0), 0); // OFF
          FindHome();
          }
      else if (user_input == '3'){
          Serial.println();
          Serial.print("Your relationship card is... ");
          PickACard();
          Relationship();
          colorWipe(strip.Color(0, 0, 0, 0), 0); // OFF
          FindHome();
          }
      else if (user_input == '4'){
          Serial.println();
          Serial.print("Your vocational card is... ");
          PickACard();
          Vocation();
          colorWipe(strip.Color(0, 0, 0, 0), 0); // OFF
          FindHome();
          }          
         }
        }

//****************************************************************************
//****************************************************************************

void FindHome(){
  digitalWrite(dir, LOW); //Pull direction pin low to move "forward"
  digitalWrite(MS1, HIGH); 
  digitalWrite(MS2, HIGH);  //Pull MS1, and MS2 high to set logic to 1/8th microstep resolution
  Serial.println("Searching for home...");
    for(x= 0; x< 500 ; x++)  //Loop forward enough times to stop a false home
  {
    digitalWrite(stp,HIGH); //Trigger one step forward
    delayMicroseconds(1275);
    digitalWrite(stp,LOW); //Pull step pin low so it can be triggered again
    delayMicroseconds(1275);
  }
  while(digitalRead(HOME_SENSOR)){
    digitalWrite(stp,HIGH); //Trigger one step forward
    delayMicroseconds(1275);
    digitalWrite(stp,LOW); //Pull step pin low so it can be triggered again
    delayMicroseconds(1275);
  }
  Serial.println("Home Found");
  Serial.println();
  Serial.println("What type of reading would you like?");
  Serial.println();
  Serial.println("1: Card of the day");
  Serial.println("2: Past, Present, Future");      
  Serial.println("3: Love and relationships");
  Serial.println("4: Career / Vocation");
 }

//****************************************************************************

void Reset(){
  digitalWrite(dir, LOW); //Pull direction pin low to move "forward"
  digitalWrite(MS1, HIGH); //Pull MS1, and MS2 high to set logic to 1/8th microstep resolution
  digitalWrite(MS2, HIGH);
    for(x= 0; x< 500 ; x++)  //Loop forward enough times to stop a false home
  {
    digitalWrite(stp,HIGH); //Trigger one step forward
    delayMicroseconds(1275);
    digitalWrite(stp,LOW); //Pull step pin low so it can be triggered again
    delayMicroseconds(1275);
  }
  while(digitalRead(HOME_SENSOR)){
    digitalWrite(stp,HIGH); //Trigger one step forward
    delayMicroseconds(1275);
    digitalWrite(stp,LOW); //Pull step pin low so it can be triggered again
    delayMicroseconds(1275);
  }
} 

//****************************************************************************

void CardOfTheDay(){
  printer.feed(1);
  printer.boldOn();
  printer.justify('C');
  printer.setSize('L');
  printer.upsideDownOn();
  printer.println(F("CARD OF THE DAY"));
  printer.feed(4);
  printer.upsideDownOff();
  printer.sleep();      // Tell printer to sleep
  delay(3000L);         // Sleep for 3 seconds
  printer.wake();       // MUST wake() before printing again, even if reset
  printer.setDefault(); // Restore printer to defaults
}

//****************************************************************************

void Past(){
  printer.feed(1);
  printer.boldOn();
  printer.justify('C');
  printer.setSize('L');
  printer.upsideDownOn();
  printer.println(F("PAST"));
  printer.feed(4);
  printer.upsideDownOff();
  printer.sleep();      // Tell printer to sleep
  delay(3000L);         // Sleep for 3 seconds
  printer.wake();       // MUST wake() before printing again, even if reset
  printer.setDefault(); // Restore printer to defaults
}

//****************************************************************************

void Present(){
  printer.feed(1);
  printer.boldOn();
  printer.justify('C');
  printer.setSize('L');
  printer.upsideDownOn();
  printer.println(F("PRESENT"));
  printer.feed(4);
  printer.upsideDownOff();
  printer.sleep();      // Tell printer to sleep
  delay(3000L);         // Sleep for 3 seconds
  printer.wake();       // MUST wake() before printing again, even if reset
  printer.setDefault(); // Restore printer to defaults
}

//****************************************************************************

void Future(){
  printer.feed(1);
  printer.boldOn();
  printer.justify('C');
  printer.setSize('L');
  printer.upsideDownOn();
  printer.println(F("FUTURE"));
  printer.feed(4);
  printer.upsideDownOff();
  printer.sleep();      // Tell printer to sleep
  delay(3000L);         // Sleep for 3 seconds
  printer.wake();       // MUST wake() before printing again, even if reset
  printer.setDefault(); // Restore printer to defaults
}

//****************************************************************************

void Relationship(){
  printer.feed(1);
  printer.boldOn();
  printer.justify('C');
  printer.setSize('M');
  printer.upsideDownOn();
  printer.println(F("RELATIONSHIP CARD"));
  printer.feed(4);
  printer.upsideDownOff();
  printer.sleep();      // Tell printer to sleep
  delay(3000L);         // Sleep for 3 seconds
  printer.wake();       // MUST wake() before printing again, even if reset
  printer.setDefault(); // Restore printer to defaults
}

//****************************************************************************

void Vocation(){
  printer.feed(1);
  printer.boldOn();
  printer.justify('C');
  printer.setSize('L');
  printer.upsideDownOn();
  printer.println(F("VOCATION CARD"));
  printer.feed(4);
  printer.upsideDownOff();
  printer.sleep();      // Tell printer to sleep
  delay(3000L);         // Sleep for 3 seconds
  printer.wake();       // MUST wake() before printing again, even if reset
  printer.setDefault(); // Restore printer to defaults
}

//****************************************************************************

void PickACard()
{
  
  int val = 
  
  Entropy.random(40, 1581); // Full range of cards excluding "blanks" #0 & #20
  int rem;
 
  rem = val % 20 ;
  if (rem < 10)
    val -= rem;
  else
    val += 20 - rem; //ensure value is a multiple of 20
 
  digitalWrite(dir, LOW); //Pull direction pin low to move "forward"
  digitalWrite(MS1, HIGH); //Pull MS1, and MS2 high to set logic to 1/8th microstep resolution
  digitalWrite(MS2, HIGH);
  for(x= 0; x< val + 1600 ; x++)  // Move spindle one full rotation before going to the choosen card
  {
    digitalWrite(stp,HIGH); 
    delayMicroseconds(1275); // delay dictates how fast the spindle rotates.
    digitalWrite(stp,LOW); 
    delayMicroseconds(1275);
  }
  
//****************************************************************************
//****************************************************************************

// LED CODE

    if (val > 20 && val < 470) { // Major Arcana
      
      Yellow(1);
}
    else if (val > 471 && val < 750) { // Wands element: Fire
      
      Red(1);
}
    else if (val > 751 && val < 1030) { // Cups element: Water
      
      Blue(1);
}
    else if (val > 1031 && val < 1310) { // Swords element: Air
      
      White(1);
}
    else if (val > 1311 && val < 1600) { // Pentacles element: Earth
      
      Green(1);
}
//****************************************************************************
//****************************************************************************

// PRINTER CODE

  if (val == 40) // The Fool
  { 
  int readingType = Entropy.random(0, 31);
  Serial.print("The Fool");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("0_p", FILE_READ);
      printer.printBitmap(384, 660, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("0_o", FILE_READ);
      printer.printBitmap(384, 660, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("0_n", FILE_READ);
      printer.printBitmap(384, 660, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 60) // The Magician
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31); 
  Serial.print("The Magician");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("1_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("1_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("1_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }
  
  else if (val == 80) // The High Priestess
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("The High Priestess");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("2_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("2_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("2_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }
  
  else if (val == 100) // The Empress
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("The Empress");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("3_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("3_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("3_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 120) // The Emperor
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);   
  Serial.print("The Emperor");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("4_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("4_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("4_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 140) // The Hierophant
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("The Hierophant");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("5_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("5_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("5_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 160) // The Lovers
  {
//   int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);   
  Serial.print("The Lovers");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("6_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("6_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("6_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 180) // The Chariot
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);   
  Serial.print("The Chariot");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("7_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("7_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("7_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 200) // Strength
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);
  Serial.println("Strength");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("8_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("8_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("8_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 220) // The Hermit
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("The Hermit");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("9_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("9_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("9_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 240) // Wheel of Fortune
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("Wheel of Fortune");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("10_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("10_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("10_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 260) // Justice
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("Justice");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("11_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("11_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("11_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 280) // The Hanged Man
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("The Hanged Man");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("12_p", FILE_READ);
      printer.printBitmap(384, 660, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("12_o", FILE_READ);
      printer.printBitmap(384, 660, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("12_n", FILE_READ);
      printer.printBitmap(384, 660, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 300) // Death
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("Death");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("13_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("13_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("13_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 320) // Temperance
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("Temperance");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("14_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("14_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("14_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 340) // The Devil
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("The Devil");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("15_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("15_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("15_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }  

  else if (val == 360) // The Tower
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("The Tower");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("16_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("16_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("16_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

  else if (val == 380) //The Star
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31);  
  Serial.print("The Star");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("17_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("17_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 20)
    {
      Serial.print("Reading type is: Reversed");
      Serial.println("");
      File data = SD.open("17_n", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
  }

    else if (val == 400) // The Moon
  {
//  int readingType = 5;
//  int readingType = 15;
//  int readingType = 25;  
  int readingType = Entropy.random(0, 31); 
  Serial.print("The Moon");
  Serial.println("");
  Serial.println("");
    if (readingType <= 9)
    {
      Serial.print("Reading type is: Upright");
      Serial.println("");
      File data = SD.open("18_p", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
    else if (readingType >= 10 && readingType <= 19)
    {
      Serial.print("Reading type is: Open");
      Serial.println("");
      File data = SD.open("18_o", FILE_READ);
      printer.printBitmap(384, 661, dynamic_cast<Stream*>(&data));
      data.close();
    }
...

This file has been truncated, please download it to see its full contents.

Custom parts and enclosures

Automated Tarot Machine Baseplate
Baseplate for the ATM where electronics should be mounted
Automated Tarot Machine Crossbar Covers
These keep the brass crossbar from rotating as well as covering the ends
Automated Tarot Machine Cover
This goes over the electronics and it serves as a stand for the split flap unit

Schematics

Circuit Diagram
Circuit diagram 5mwvln4yrf

Comments

Similar projects you might like

Automated NERF Gun Shooting Gallery

Project showcase by Keegan Neave

  • 4,624 views
  • 1 comment
  • 20 respects

Automated Model Railway Layout with Passing Siding

Project tutorial by Kushagra Keshari

  • 11,745 views
  • 11 comments
  • 44 respects

Arduino101 / tinyTILE BLE: Match-Making Sunglasses

Project tutorial by Kitty Yeung

  • 14,965 views
  • 5 comments
  • 46 respects

Simple Automated Model Railway Layout | Arduino Controlled

Project tutorial by Kushagra Keshari

  • 9,947 views
  • 1 comment
  • 16 respects

Arduino-Based Bitcoin Candy Vending Machine

Project tutorial by Team Elkrem

  • 9,604 views
  • 19 comments
  • 57 respects

Washing Machine Timer

Project tutorial by Shahariar

  • 4,608 views
  • 5 comments
  • 11 respects
Add projectSign up / Login