Djair Guilherme
Published © GPL3+

Paper, Rock, Scissor Game with Grove Vision AI V2

Challenge artificial intelligence with a game of Paper, Rock, Scissors

BeginnerFull instructions provided1 hour237
Paper, Rock, Scissor Game with Grove Vision AI V2

Things used in this project

Hardware components

Grove Vision AI Module V2
Seeed Studio Grove Vision AI Module V2
×1
Seeed Studio XIAO ESP32S3 Sense
Seeed Studio XIAO ESP32S3 Sense
×2

Software apps and online services

Arduino IDE
Arduino IDE
Home Assistant
Home Assistant
EspHome

Story

Read more

Code

ESPHome Yaml File for Prize Unit

YAML
This an config file for an ESP Home custom Prize unit
substitutions:
  name: prize
  friendly_name: prize
  mac_light: "mac_address_of_this_board"
  # Service
  light_control_service_uuid: https://www.uuidgenerator.net/
  # Characteristic
  light_virtual_switch_uuid: https://www.uuidgenerator.net/
  light_toggle_command: prize-toggle
  light_virtual_switch_id: prize_virtual_switch
  
esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  name_add_mac_suffix: false
  project:
    name: esphome.web
    version: '1.0'

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: arduino

# Enable logging
logger:

# Variaveis globais com o setup da dosagem para alterar no Home Assistant
globals:
  - id: dosage_count
    type: int
    restore_value: yes
    initial_value: "0"

api: 
  encryption:
    key: "my_api_key"
  
  # Adicionando método para ajustar dosagem no Home Assistant
  services:
  - service: set_dosage
    variables:
      my_count: int
    then:
      - globals.set:
          id: dosage_count
          value: !lambda 'return my_count;'


# Allow Over-The-Air updates
ota:
  platform: esphome

# Allow provisioning Wi-Fi via serial
improv_serial:

wifi:
  # Set up a wifi access point
  ap: {}

# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:

dashboard_import:
  package_import_url: github://esphome/firmware/esphome-web/esp32c3.yaml@v2
  import_full_config: true

# Sets up Bluetooth LE (Only on ESP32) to allow the user
# to provision wifi credentials to the device.
#esp32_improv:
#  authorizer: none

# To have a "next url" for improv serial
web_server:


# Configurando Buzzer Também
output:
  - platform: ledc
    pin: GPIO3 # Pin D1
    id: rtttl_out

rtttl:
  output: rtttl_out
  on_finished_playback:
    - logger.log: 'Song ended!'

# Gerando Sensor BLE
switch:
  - platform: template
    id: ${light_virtual_switch_id}
    optimistic: true
    turn_on_action:
      - logger.log: "Sensor via Bluetooth ON"
      - switch.turn_on: dosador
    turn_off_action:
      - logger.log: "Sensor via Bluetooth OFF"
      - switch.turn_off: dosador
 
# Ativando o Motor do Dosador no pino D2 - 4 
  - platform: gpio
    name: "Motor Dosador"
    pin: GPIO4
    # Dosador ID é o motor do dosador.
    id: prize
    restore_mode: ALWAYS_OFF
    on_turn_on:
      - logger.log: "Acionando Dosador"
      - rtttl.play: 'two_short:d=4,o=4,b=180:16e6,16e6'
    on_turn_off:
      - logger.log: "Desligando Dosador"

number:
  - platform: template
    name: "Ajustar Dosagem"
    id: ajustar_dosagem
    optimistic: true
    min_value: 1
    max_value: 8
    step: 1
    on_value:
      then:
        - globals.set:
            id: dosage_count
            value: !lambda 'return int(x);'


# Detecta o pressionamento do Sensor de Dosagem D0 - 2
binary_sensor:   
  - platform: gpio
    name: "Sensor de Dosagem"

    pin:
      number: GPIO2
      mode:
        input: true
        pullup: true

    on_release:
      lambda: |-
        static int num_executions = 0;
        num_executions += 1;

        if (num_executions == id(dosage_count)) {
          ESP_LOGD("main", "Stoping Motor at %d dosage", num_executions);    
          id (dosador).turn_off();
          num_executions = 0;
        } 


external_components:
  - source: github://wifwucite/esphome-ble-controller

esp32_ble_controller:
  security_mode: none
  services:
    - service: ${light_control_service_uuid}
      characteristics:
        - characteristic: ${light_virtual_switch_uuid}
          exposes: ${light_virtual_switch_id}

  commands:
    - command: ${light_toggle_command}
      description: Liga o Dosador via Bluetooth
      on_execute:
        - logger.log: "Get prize"
        - switch.toggle: dosador

  on_connected:
    - logger.log: "BLE Conectado"
  on_disconnected:
    - logger.log: "BLE Desconectado"

Paper Rock Scissor with AI

Arduino
/**
  Simple Paper, Rock, Scissor Game
  2024 - By Djair Guilherme @nicolaudosbrinquedos
  Using Grove Vision AI V2 - with Seeed XIAO ESP32-S3
  and Gesture Detection Model

  https://github.com/Seeed-Studio/sscma-model-zoo/blob/main/docs/en/Gesture_Detection_Swift-YOLO_192.md
*/



String myGame, yourGame;


#ifdef ESP32
#include <HardwareSerial.h>
HardwareSerial atSerial(0);

#else
#define atSerial Serial1
#endif

// Communication with Grove Vision AI V2

#include <Seeed_Arduino_SSCMA.h>
SSCMA GrooveAI;

// The inference returns Index of Object, not String

#define  PAPER_TARGET   0
#define  ROCK_TARGET   1
#define  SCISSOR_TARGET   2

bool is_paper = false;
bool is_rock = false;
bool is_scissor = false;

int computerGuess = -1;
int score = 0;
int found = -1;
int lastGuess = -1;
unsigned long lastGuessTime = 0;

void setup()
{
  randomSeed(analogRead(0));
  GrooveAI.begin(&atSerial, D3);
  Serial.begin(115200);
  delay(2000);
  Serial.println("PAPER ROCK SCISSOR GAME WITH AI");
}


void loop() {

  if (!GrooveAI.invoke(1, false, true)) {
    bool found = false;
    int playerGuess = -1;

    // Last Guess Time
    unsigned long currentTime = millis();
    unsigned long elapsedTime = currentTime - lastGuessTime;

    // For each Box, detect if is PAPER ROCK or SCISSOR
    for (int i = 0; i < GrooveAI.boxes().size(); i++) {

      if (GrooveAI.boxes()[i].target == ROCK_TARGET) {
        is_rock = true;
        playerGuess = ROCK_TARGET;
      } else if (GrooveAI.boxes()[i].target == PAPER_TARGET) {
        is_paper = true;
        playerGuess = PAPER_TARGET;
      } else if (GrooveAI.boxes()[i].target == SCISSOR_TARGET) {
        is_scissor = true;
        playerGuess = SCISSOR_TARGET;
      } else {
        is_rock = false;
        is_paper = false;
        is_scissor = false;
        found = false;
      }
    }

    // Update 'found' only if playerGuess is different than before or if 3s of last guess
    if ((playerGuess != lastGuess || elapsedTime > 3000)  && playerGuess != -1) {
      found = true;
      lastGuess = playerGuess;
    }

    if (found) {
      // Create Computer Guess after inference
      computerGuess = random(0, 3);
      Serial.println("+++++++++++++++");
      String yourGame = (playerGuess == 0) ? "PAPER" :
                       (playerGuess == 1) ? "ROCK" : "SCISSOR";
      String myGame = (computerGuess == 0) ? "PAPER" :
                       (computerGuess == 1) ? "ROCK" : "SCISSOR";

      int result = determine_winner(playerGuess, computerGuess);

      if (result == 2) {
        Serial.println("DRAW");
      } else if (result == 1) {
        Serial.println("Human WIN");
      } else {
        Serial.println("AI WIN");
      }

      Serial.print("Your guess is ");
      Serial.print(yourGame);
      Serial.print(" and mine is ");
      Serial.println(myGame);
      Serial.println("+++++++++++++++");
      Serial.println();
      
      // Reset conditions
      is_rock = false;
      is_paper = false;
      is_scissor = false;
      found = false;    
    }
  }
  delay(1000);
}

// WINNING CONDITIONS

int determine_winner(int playerGuess, int computerGuess) {
  if (playerGuess == computerGuess) {
    return 2; // Empate
  } else if ((playerGuess == 0 && computerGuess == 1) || // Papel contra Pedra
             (playerGuess == 1 && computerGuess == 2) || // Pedra contra Tesoura
             (playerGuess == 2 && computerGuess == 0)) { // Tesoura contra Papel
    return 1; // playerGuess vence
  } else {
    return 0; // computerGuess vence
  }
}

Paper Rock Scissor with Bluetooth Prize Dispenser

Arduino
Same game, with Bluetooth Connection and Prize Dispenser
/**
  Simple Paper, Rock, Scissor Game
  2024 - By Djair Guilherme @nicolaudosbrinquedos
  Using Grove Vision AI V2 - with Seeed XIAO ESP32-S3
  and Gesture Detection Model

  Access Bluetooth Device to give a prize to winner

  https://github.com/Seeed-Studio/sscma-model-zoo/blob/main/docs/en/Gesture_Detection_Swift-YOLO_192.md

*/

// Periferico Bluetooth
#include "BLEDevice.h"
// Get an UUID at https://www.uuidgenerator.net/

static BLEUUID serviceUUID("https://www.uuidgenerator.net/");
static BLEUUID activateUUID("https://www.uuidgenerator.net/");
static BLERemoteCharacteristic* bluetoothPrize;

// Communication with Grove Vision AI V2

#include <Seeed_Arduino_SSCMA.h>
SSCMA GrooveAI;

// The inference returns Index of Object, not String

#define  PAPER_TARGET   0
#define  ROCK_TARGET   1
#define  SCISSOR_TARGET   2

#ifdef ESP32
#include <HardwareSerial.h>
HardwareSerial atSerial(0);

#else
#define atSerial Serial1
#endif

bool is_paper = false;
bool is_rock = false;
bool is_scissor = false;

int computerGuess = -1;
int score = 0;
int found = -1;
int lastGuess = -1;
unsigned long lastGuessTime = 0;

void setup()
{
  randomSeed(analogRead(0));
  GrooveAI.begin(&atSerial, D3);
  Serial.begin(115200);
  delay(2000);
  Serial.println("ROCK PAPER SCISSOR BLUETOOTH PRIZE");


  BLEDevice::init("");
  BLEClient*  pClient = BLEDevice::createClient();

// Get the Mac Address of Prize Dispenser

  if (pClient->connect(BLEAddress("mac_address_prize"))) {
    Serial.println("Connected to Prize Dispenser");
  } else Serial.println("Cant connect to Prize Dispenser");

  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.println("Cant find Service");
    return;
  } else Serial.println("Service Found");

  bluetoothPrize = pRemoteService->getCharacteristic(activateUUID);
  if (bluetoothPrize == nullptr) {
    Serial.println("Cant found Characteristic");
    return;
  } else Serial.println("Characteristic Found");
}


void loop() {

  if (!GrooveAI.invoke(1, false, true)) {
    bool found = false;
    int playerGuess = -1;

    // Last Guess Time
    unsigned long currentTime = millis();
    unsigned long elapsedTime = currentTime - lastGuessTime;

    // For each Box, detect if is PAPER ROCK or SCISSOR
    for (int i = 0; i < GrooveAI.boxes().size(); i++) {

      if (GrooveAI.boxes()[i].target == ROCK_TARGET) {
        is_rock = true;
        playerGuess = ROCK_TARGET;
      } else if (GrooveAI.boxes()[i].target == PAPER_TARGET) {
        is_paper = true;
        playerGuess = PAPER_TARGET;
      } else if (GrooveAI.boxes()[i].target == SCISSOR_TARGET) {
        is_scissor = true;
        playerGuess = SCISSOR_TARGET;
      } else {
        is_rock = false;
        is_paper = false;
        is_scissor = false;
        found = false;
      }
    }

    // Update 'found' only if playerGuess is different than before or if 3s of last guess
    if ((playerGuess != lastGuess || elapsedTime > 3000)  && playerGuess != -1) {
      found = true;
      lastGuess = playerGuess;
    }

    if (found) {
      // Create Computer Guess after inference
      computerGuess = random(0, 3);
      Serial.println("+++++++++++++++");
      String yourGame = (playerGuess == 0) ? "PAPER" :
                       (playerGuess == 1) ? "ROCK" : "SCISSOR";
      String myGame = (computerGuess == 0) ? "PAPER" :
                       (computerGuess == 1) ? "ROCK" : "SCISSOR";

      int result = determine_winner(playerGuess, computerGuess);

      if (result == 2) {
        Serial.println("DRAW");
      } else if (result == 1) {
        Serial.println("Human WIN");
        winnerPrize();
      } else {
        Serial.println("AI WIN");
      }

      Serial.print("Your guess is ");
      Serial.print(yourGame);
      Serial.print(" and mine is ");
      Serial.println(myGame);
      Serial.println("+++++++++++++++");
      Serial.println();
      
      // Reset conditions
      is_rock = false;
      is_paper = false;
      is_scissor = false;
      found = false;    
    }
  }
  delay(1000);
}

// WINNING CONDITIONS

int determine_winner(int playerGuess, int computerGuess) {
  if (playerGuess == computerGuess) {
    return 2; // Empate
  } else if ((playerGuess == 0 && computerGuess == 1) || // Papel contra Pedra
             (playerGuess == 1 && computerGuess == 2) || // Pedra contra Tesoura
             (playerGuess == 2 && computerGuess == 0)) { // Tesoura contra Papel
    return 1; // playerGuess vence
  } else {
    return 0; // computerGuess vence
  }
}


void winnerPrize() {
  bluetoothPrize->writeValue((byte)0x01);
  delay(1000);
  bluetoothPrize->writeValue((byte)0x00);
}

Credits

Djair Guilherme
19 projects • 17 followers
Brazilian artist. Puzzle maker, Arduino lover.
Contact
Thanks to Seeed Studio .

Comments

Please log in or sign up to comment.