Front view of the blackjack helper, without power on.
This project helps new players learn blackjack strategy. The main input method is the rotary encoder, which allows you to select a card by rotating the knob, and pressing down to confirm your selection. There are 4 buttons for resetting the hand, switching between dealer and player card select, removing a card, and getting advice to either hit or stand.
Playing a hand of blackjack, first selecting dealer's card.
LCD displaying current cards and total.
Blackjack helper advising me to stand.
Blackjack helper winning me a virtual $1000.
In this video, I show operation of the blackjack machine by helping me win an online game. At the end of the video, I scroll through and demo all the functions of the blackjack helper.
Testing the buttons and rotary encoder work with the code before adding the LCD screen.
Verifying the LCD screen works with the rotary encoder and buttons.
Test fitting the LCD screen and buttons on the acrylic case.
Fitting the components to the base of the case, ready for final assembly.
The design process for this blackjack helper was really enjoyable. The rotary encoder I used in this project has clicks to each segment, which was really satisfying to play around with. In comparison to a previous project, I used a different rotary encoder that didn't have any clicks, and it didn't work nearly as well as this one. I was originally going to lasercut the case out of wood, but in the laser cutting room, I saw a really nice sheet of blue acrylic and thought it looked really nice, so I chose to use that instead.
I really enjoyed this project, I think I'm the most proud of it out of all the projects I've built in this class so far. It's a solution (kind of) to a problem my friends and I have when we sometimes gamble online. When you're hyped up on adrenaline while playing a $10 hand of blackjack, it becomes a bit difficult to make the correct decision or as they call it, "play by the book". Therefore, I decided to try and make a small box that could tell me how to play the game. I initially came up with an idea of a box that had 6 buttons to add a card, increment the card, decrement the card, switch between the dealer and the player card select, reset the hand, and ask for help. I quickly realized this was way too many buttons to have on a single small box, so I had to do some rethinking. I decided it would be nice to use a rotary encoder for the card increment/decrement, and luckily during our peer reviews, my classmates gave me some great ideas.
"Automatic switching between player/dealer adding cards" was the first suggestion, which I thought was a genius idea. In a real game of blackjack, you only get to see the dealer's first card until the end, so I implemented it so that the blackjack helper would automatically switch to the player input after selecting the dealer's card. Another great suggestion was "Add a remove card button for accidents". The way I had it set up currently, there was no remove card button and I would have to reset the hand in order to fix an input mistake. After I implemented these fixes, I found that the blackjack helper was not as helpful as I thought it would be when actually using it. First, the box is incredibly bulky because I had to account for the wires inside taking up a ton of vertical space, which I had to design for. Second, I felt like my own intuition when playing a hand of blackjack gave me a higher winrate than blindly following what the box said, which kind of defeats the purpose.
If I were to build a second iteration of this project, the first thing I would do is design a PCB or use a soldered protoboard to shrink the size of the box down to be more portable. Second, I would want to implement a card counting feature where it would take into account the history of the cards played in the past, which would lead to more realistic advice. I would have implemented it in the first place, but it proved to be too difficult for me to think of a way to implement it in code, not to mention I didn't even know how to card count in real life. I'm still pretty happy with the way the project turned out, I really love the blue acrylic, I think I will look for more of these colorful materials in the future to use in my personal projects.
/*
Blackjack Helper for Arduino
Caden Yang 11/13/25
This code is a simple Blackjack helper tool designed for use with an Arduino and a rotary encoder.
It allows a user to simulate a Blackjack game by selecting cards for the dealer and player.
The tool provides advice based on standard Blackjack strategy based on the dealer's up card and the player's hand value.
Pin Mapping:
Arduino Pin | Role | Description
-----------------------------------------------------------
2 | Input | NEW_HAND_PIN - Start a new hand
3 | Input | HELP_PIN - advice hit/stand
4 | Input | TOGGLE_ROLE_PIN - Switch between dealer and player modes
5 | Input | REMOVE_CARD_PIN - Remove last card from player's hand
7 | Input | ENC_CLK - Rotary encoder clock signal
8 | Input | ENC_DT - Rotary encoder data signal
9 | Input | ENC_SW - Rotary encoder switch button (to select card)
Code written mostly by ChatGPT, including helper functions, LCD display, Serial printing, rotary encoder logic.
*/
#include <Arduino.h>
const int NEW_HAND_PIN = 2;
const int HELP_PIN = 3;
const int TOGGLE_ROLE_PIN = 4;
const int REMOVE_CARD_PIN = 5;
const int ENC_CLK = 7;
const int ENC_DT = 8;
const int ENC_SW = 9;
int dealerCard = -1;
int playerCards[10];
int playerCardCount = 0;
int currentCard = 2; // current card being selected
bool selectingDealer = true; // true = dealer, false = player
// Rotary encoder state
int lastCLKState;
unsigned long lastButtonPress = 0;
const char* cardNames[] = {
"Ace", "2", "3", "4", "5", "6",
"7", "8", "9", "10", "Jack", "Queen", "King"
};
int cardValue(int card) {
if (card == 1) return 11;
if (card >= 10) return 10;
return card;
}
int handValue() {
int total = 0;
int aces = 0;
for (int i = 0; i < playerCardCount; i++) {
int v = cardValue(playerCards[i]);
total += v;
if (playerCards[i] == 1) aces++;
}
while (total > 21 && aces > 0) {
total -= 10;
aces--;
}
return total;
}
String getAdvice(int dealer, int total) {
int dealerVal = cardValue(dealer);
if (total >= 17) return "STAND";
if (total <= 11) return "HIT";
if (total == 12) {
if (dealerVal >= 4 && dealerVal <= 6) return "STAND";
else return "HIT";
}
if (total >= 13 && total <= 16) {
if (dealerVal >= 2 && dealerVal <= 6) return "STAND";
else return "HIT";
}
return "HIT";
}
void displayState(String advice = "") {
Serial.println("----- BLACKJACK HELPER -----");
Serial.print("Mode: ");
Serial.println(selectingDealer ? "DEALER" : "PLAYER");
Serial.print("Dealer Card: ");
if (dealerCard == -1) Serial.println("(none)");
else Serial.println(cardNames[dealerCard - 1]); // adjust index
Serial.print("Player Cards: ");
if (playerCardCount == 0) Serial.println("(none)");
else {
for (int i = 0; i < playerCardCount; i++) {
Serial.print(cardNames[playerCards[i] - 1]); // adjust index
if (i < playerCardCount - 1) Serial.print(", ");
}
Serial.println();
}
if (advice != "") {
Serial.print("ADVICE: ");
Serial.println(advice);
}
Serial.println("-----------------------------\n");
}
void setup() {
Serial.begin(9600);
pinMode(NEW_HAND_PIN, INPUT_PULLUP);
pinMode(HELP_PIN, INPUT_PULLUP);
pinMode(TOGGLE_ROLE_PIN, INPUT_PULLUP);
pinMode(REMOVE_CARD_PIN, INPUT_PULLUP);
pinMode(ENC_CLK, INPUT);
pinMode(ENC_DT, INPUT);
pinMode(ENC_SW, INPUT_PULLUP);
lastCLKState = digitalRead(ENC_CLK);
Serial.println("Blackjack Helper Ready!");
Serial.println("Start by selecting the dealer’s card.\n");
displayState();
}
void loop() {
// === ROTARY ENCODER ROTATION ===
int currentCLKState = digitalRead(ENC_CLK);
if (currentCLKState != lastCLKState && currentCLKState == HIGH) {
if (digitalRead(ENC_DT) == HIGH) {
currentCard++;
if (currentCard > 13) currentCard = 1;
} else {
currentCard--;
if (currentCard < 1) currentCard = 13;
}
Serial.print("Current card: ");
Serial.println(cardNames[currentCard - 1]); // adjust index
}
lastCLKState = currentCLKState;
// === ENCODER BUTTON (SELECT CARD) ===
if (digitalRead(ENC_SW) == LOW) {
if (millis() - lastButtonPress > 250) {
if (selectingDealer) {
dealerCard = currentCard;
Serial.print("Dealer card set to: ");
Serial.println(cardNames[dealerCard - 1]);
selectingDealer = false; // auto switch to player
Serial.println("→ Switched to player mode.\n");
} else {
if (playerCardCount < 10) {
playerCards[playerCardCount++] = currentCard;
Serial.print("Added player card: ");
Serial.println(cardNames[currentCard - 1]);
} else {
Serial.println("Max player cards reached!");
}
}
displayState();
lastButtonPress = millis();
}
}
// === BUTTON CONTROLS ===
if (digitalRead(TOGGLE_ROLE_PIN) == LOW) {
selectingDealer = !selectingDealer;
Serial.print("Switched mode → ");
Serial.println(selectingDealer ? "DEALER" : "PLAYER");
displayState();
delay(250);
}
if (digitalRead(REMOVE_CARD_PIN) == LOW && !selectingDealer && playerCardCount > 0) {
playerCardCount--;
Serial.println("Removed last player card.");
displayState();
delay(250);
}
if (digitalRead(NEW_HAND_PIN) == LOW) {
dealerCard = -1;
playerCardCount = 0;
selectingDealer = true;
Serial.println("New hand started! Select dealer card.");
displayState();
delay(250);
}
// === HELP BUTTON ===
if (digitalRead(HELP_PIN) == LOW) {
if (dealerCard == -1 || playerCardCount == 0) {
Serial.println("Please set dealer and player cards first!");
} else {
int total = handValue();
String advice = getAdvice(dealerCard, total);
Serial.print("HELP: Total ");
Serial.print(total);
Serial.print(" vs Dealer ");
Serial.print(cardNames[dealerCard - 1]);
Serial.print(": ");
Serial.println(advice);
displayState(advice);
}
delay(500);
}
}