Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:prj2023:adarmaz:kings-cup [2023/05/30 02:56]
mihnea.branzeu [Descriere generală]
pm:prj2023:adarmaz:kings-cup [2023/05/30 09:24] (current)
mihnea.branzeu [Rezultate Obţinute]
Line 61: Line 61:
  
 ===== Software Design ===== ===== Software Design =====
 +Librarii folosite:
 +  * **LiquidCrystal_I2C.h** - folosita pentru interfatarea cu LCD-ul
 +  * **esp_now.h** si **WiFi.h** - folosite pentru comunicarea inter-modul
  
 +In cadrul proiectului am fost nevoit sa scriu doua programe separate, unul care sa ruleze pe modulul master, iar celalalt pentru a rula pe fiecare dintre modulele player.
  
-<note tip+==== Comunicarea inter-ESP32 ==== 
-Descrierea codului aplicaţiei ​(firmware): +Pentru a permite comunicarea intre cele 4 module ESP32 am folosit biblioteca **esp-now.h**. Aceasta foloseste adresa MAC a ESPului pentru a identifica statia in retea. Astfel, am avut nevoie de un script cu ajutorul caruia sa aflu adresa MAC a fiecarui modul: 
-  * mediu de dezvoltare ​(if any) (e.gAVR StudioCodeVisionAVR+ 
-  * librării şsurse 3rd-party ​(e.gProcyon AVRlib+<code cpp
-  * algoritmi şstructuri pe care plănuiţi să le implementaţ+#include "​WiFi.h"​ 
-  * (etapa 3) surse şfuncţii implementate + 
-</note>+void setup() 
 +  // Setup Serial Monitor 
 +  Serial.begin(9600);​ 
 + 
 +  // Put ESP32 into Station mode 
 +  WiFi.mode(WIFI_MODE_STA);​ 
 + 
 +  // Print MAC Address to Serial monitor 
 +  Serial.print("​MAC Address"); 
 +  ​Serial.println(WiFi.macAddress());​ 
 +
 + 
 +void loop() {} 
 +</​code>​ 
 + 
 + 
 +Avand adresa MAC, am trecut la a construi un standard de comunicare. Am obtinut urmatoarea configuratie:​ 
 + 
 +<code cpp> 
 +#define MESSAGE_DRINK 0 
 +#define MESSAGE_CONFIRM 1 
 +#define MESSAGE_SCREAM 2 
 +#define MESSAGE_SAY 3 
 +#define MESSAGE_GO_NEXT 4 
 +#define MESSAGE_THUMB_MASTER 5 
 +#define MESSAGE_WATERFALL_GO 6 
 +#define MESSAGE_WATERFALL_STOP 7 
 +#define MESSAGE_WATERFALL_GO_INITIATOR 8 
 + 
 +typedef struct message_t { 
 +  int sender; 
 +  int receiver; 
 +  int messageType;​ 
 +} message_t;​ 
 +</​code>​ 
 + 
 +Cu ajutorul define-urilor,​ am putut sa impart mesajele in mai multe categorii, acestea completand campul ​**messageType** din cadrul structurii. De asemenea, structura **message_t** contine layout-ul unui mesaj intre doua module: senderul (sursa mesajului), receiverul (destinatia mesajului) si tipul mesajului. Acest segment ​de cod trebuie sa se regaseasca atat pe master, cat si pe player. 
 + 
 +Mai departe, cu ajutorul functiilor **OnDataSent()**, **OnDataReceived()** si **esp_now_send()**,​ oferite de biblioteca, am putut realiza comunicarea intre module. 
 + 
 +==== Cod Master ==== 
 +Modulul Master este realizat sub forma unui state-machine,​ avand urmatoarele stari: 
 +<code cpp> 
 +#define NEW_GAME_STATE 0 
 +#define NEW_CARD_STATE 1 
 +#define WAITING_FOR_DRINKS_STATE 2 
 +#define GAME_OVER_STATE 3 
 +#define IDLE_STATE 4 
 +#define CHOOSING_SOMEONE_STATE 5 
 +#define COUNTDOWN_STATE 6 
 +#define THUMB_MASTER_STATE 7 
 +#define WATERFALL_STATE 8 
 +</​code>​ 
 +Logica este alcatuita din urmatorii pasi: 
 +  - O carte este generata random 
 +  - Functia corespunzatoare cartii este apelata si provocarea incepe 
 +  - Playerii care trebuie sa bea sunt anuntati, si se asteapta ca ei sa execute provocarea 
 +  - Atunci cand se doreste trecerea la urmatoarea carte, functia de verificare este apelata 
 +  - Daca toti jucatorii au indeplinit provocarea, se revine la pasul **1** 
 + 
 +Un jucator este definit sub forma unei structuri astfel: 
 +<code cpp> 
 +// The structure containing info about players 
 +typedef struct player_t { 
 +  uint8_t macAdress[6];​ // The MAC address used for communication 
 +  int gender; // Used for special challenges 
 +  bool shouldDrink;​ // Flag for keeping track of the status for each player 
 +  bool thumbMaster;​ // Flag for the thumb master challenge 
 +} player_t; 
 +</​code>​ 
 + 
 +Codul integral al modulului master: 
 +<code cpp> 
 +#include <​LiquidCrystal_I2C.h>​ 
 +#include <​esp_now.h>​ 
 +#include <​WiFi.h>​ 
 + 
 +// Pin Setup 
 +#define BUTTON_1_PIN 33 
 +#define BUTTON_2_PIN 25 
 +#define BUTTON_3_PIN 26 
 +#define BUTTON_NEXT_PIN 32 
 + 
 +// States definition 
 +#define NEW_GAME_STATE 0 
 +#define NEW_CARD_STATE 1 
 +#define WAITING_FOR_DRINKS_STATE 2 
 +#define GAME_OVER_STATE 3 
 +#define IDLE_STATE 4 
 +#define CHOOSING_SOMEONE_STATE 5 
 +#define COUNTDOWN_STATE 6 
 +#define THUMB_MASTER_STATE 7 
 +#define WATERFALL_STATE 8 
 + 
 +#define CARDS_PER_NUMBER 4 
 +#define NUMBER_OF_PLAYERS 3 
 + 
 +// Message types 
 +#define MESSAGE_DRINK 0 
 +#define MESSAGE_CONFIRM 1 
 +#define MESSAGE_SCREAM 2 
 +#define MESSAGE_SAY 3 
 +#define MESSAGE_GO_NEXT 4 
 +#define MESSAGE_THUMB_MASTER 5 
 +#define MESSAGE_WATERFALL_GO 6 
 +#define MESSAGE_WATERFALL_STOP 7 
 +#define MESSAGE_WATERFALL_GO_INITIATOR 8 
 + 
 +#define GIRL 0 
 +#define BOY 1 
 + 
 +#define PLAYER_1 0 
 +#define PLAYER_2 1 
 +#define PLAYER_3 2 
 +#define MASTER 3 
 + 
 + 
 +// The structure containing info about players 
 +typedef struct player_t { 
 +  uint8_t macAdress[6];​ // The MAC address used for communication 
 +  int gender; // Used for special challenges 
 +  bool shouldDrink;​ // Flag for keeping track of the status for each player 
 +  bool thumbMaster;​ // Flag for the thumb master challenge 
 +} player_t; 
 + 
 +// The structure containing the layout of a message 
 +typedef struct message_t { 
 +  int sender; 
 +  int receiver; 
 +  int messageType;​ 
 +} message_t;​ 
 + 
 +// The players array 
 +player_t players[3];​ 
 + 
 +// The left amount of each card 
 +int cardFrequencies[15];​ 
 +int cardsLeft = (CARDS_PER_NUMBER) * 12; 
 + 
 +// The current state of the game 
 +int currentState;​ 
 +int currentTurn;​ 
 + 
 +// Variables used for countdown 
 +double a = millis(); 
 +double i = 0; 
 +double c; 
 +bool shouldRun = false; 
 +bool shouldReset = false; 
 +int currentToAnswer;​ 
 + 
 +// Variable used for thumb master 
 +int thumbMasterCount;​ 
 + 
 +// Peer info structure 
 +esp_now_peer_info_t peerInfo; 
 + 
 +LiquidCrystal_I2C lcd(0x27,​16,​2); ​ // set the LCD address to 0x3F for a 16 chars and 2 line display 
 + 
 +// Callback when data is sent 
 +void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 
 +  char macStr[18];​ 
 +  Serial.print("​Packet to: "); 
 +  // Copies the sender mac address to a string 
 +  snprintf(macStr,​ sizeof(macStr),​ "​%02x:​%02x:​%02x:​%02x:​%02x:​%02x",​ 
 +           ​mac_addr[0],​ mac_addr[1],​ mac_addr[2],​ mac_addr[3],​ mac_addr[4],​ mac_addr[5]);​ 
 +  Serial.print(macStr);​ 
 +  Serial.print("​ send status:​\t"​);​ 
 +  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "​Delivery Success"​ : "​Delivery Fail"​);​ 
 +
 + 
 +// Callback function executed when data is received 
 +void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData,​ int len) { 
 +  message_t message; 
 + 
 +  memcpy(&​message,​ incomingData,​ sizeof(message_t));​ 
 +  Serial.print("​Data received: "); 
 +  Serial.println(len);​ 
 +  Serial.print("​Sender:​ "); 
 +  Serial.println(message.sender);​ 
 +  Serial.print("​Message type: "); 
 +  Serial.println(message.messageType == 0 ? "​DRINK"​ : "​CONFIRM"​);​ 
 + 
 +  // Check the type of message 
 +  ​if (message.messageType == MESSAGE_CONFIRM
 +    // Mark the drink flag for the player 
 +    players[message.sender].shouldDrink = false; 
 +  } 
 + 
 +  if (message.messageType == MESSAGE_GO_NEXT) { 
 +    // Send the say message to the next player 
 +    shouldReset = true; 
 +    player_t nextPlayer = players[(message.sender + 1) % NUMBER_OF_PLAYERS];​ 
 +    currentToAnswer = (message.sender + 1) % NUMBER_OF_PLAYERS;​ 
 +    sendMessage((message.sender + 1) % NUMBER_OF_PLAYERSMESSAGE_SAY,​ nextPlayer.macAdress); 
 +  ​
 + 
 +  if (message.messageType == MESSAGE_THUMB_MASTER) { 
 +    // Mark the player 
 +    players[message.sender].thumbMaster = true; 
 +    thumbMasterCount++;​ 
 +  } 
 + 
 +  if (message.messageType == MESSAGE_WATERFALL_STOP) { 
 +    if ((message.sender ​ + 1) % NUMBER_OF_PLAYERS != currentTurn) { 
 +      player_t nextPlayer = players[(message.sender + 1) % NUMBER_OF_PLAYERS];​ 
 +      sendMessage((message.sender + 1) % NUMBER_OF_PLAYERS,​ MESSAGE_WATERFALL_STOP,​ nextPlayer.macAdress);​ 
 +    } 
 +    else { 
 +      currentState = WAITING_FOR_DRINKS_STATE;​ 
 +    } 
 +  } 
 +
 + 
 +void sendMessage(int receiver, int messageType,​ uint8_t ​*macAdress) { 
 +  // Create the message 
 +  message_t message = {MASTER, receiver, messageType};​ 
 +   
 +  // Send the message 
 +  esp_err_t result = esp_now_send(macAdress,​ (uint8_t *) &​message,​ sizeof(message_t));​ 
 + 
 +  // Check the result 
 +  if (result == ESP_OK) { 
 +    Serial.println("​Sent with success"​);​ 
 +  } 
 +  else { 
 +    Serial.println("​Error sending the data"​);​ 
 +  } 
 +
 + 
 +void lcdPrintAnimation() { 
 +  lcd.clear();​ 
 +  lcd.setCursor(2,​ 0); 
 +  lcd.print("​ PLAYER "); 
 +  lcd.setCursor(10,​ 0); 
 +  lcd.print(currentTurn + 1); 
 +  delay(2000);​ 
 +  lcd.clear();​ 
 +  int i, j; 
 +  lcd.setCursor(0, 0); 
 +  for (int i = 0; i < 16; i++) { 
 +    for (int j = 0; j < i; j++) { 
 +      lcd.setCursor(j,​ 0); 
 +      lcd.print("​="​)
 +      lcd.setCursor(15 - j, 1); 
 +      lcd.print("​="​);​ 
 +    } 
 +    delay(100); 
 +  ​
 +
 + 
 +void lcdPrint(char ​*message, int row, int shouldClear) { 
 +  if (shouldClear) { 
 +    lcd.clear();​ 
 +  } 
 +  lcd.setCursor(2,​ row); 
 +  lcd.print(message);​ 
 +
 + 
 +void twoChallenge() { 
 +  // Print the message on the LCD 
 +  lcdPrint("​2 IS YOU", 0, 1); 
 +  lcdPrint("​Choose Someone",​ 1, 0); 
 + 
 +  // Change the state to choosing someone 
 +  currentState = CHOOSING_SOMEONE_STATE;​ 
 +
 + 
 +void threeChallenge() { 
 +  // Print the message on the LCD 
 +  lcdPrint("​3 IS ME", 0, 1); 
 +  lcdPrint("​ Drink!!",​ 1, 0); 
 + 
 +  // Mark the player as drinker 
 +  players[currentTurn].shouldDrink = true; 
 + 
 +  // Send the message 
 +  sendMessage(currentTurn,​ MESSAGE_DRINK,​ players[currentTurn].macAdress);​ 
 +
 + 
 +void fourChallenge() { 
 +  lcdPrint("​4 IS GIRLS",​ 0, 1); 
 +  lcdPrint("​Girls Drink",​ 1, 0); 
 + 
 +  // Search for the girls 
 +  for (int = 0; < NUMBER_OF_PLAYERS;​ i++) { 
 +    if (players[i].gender == GIRL) { 
 +      // Mark the player as drinker 
 +      players[i].shouldDrink = true; 
 + 
 +      // Send the message 
 +      sendMessage(i,​ MESSAGE_DRINK,​ players[i].macAdress);​ 
 +    } 
 +  } 
 +   
 +
 + 
 +void fiveChallenge() { 
 +  lcdPrint(" ​ THUMB MASTER",​ 0, 1); 
 +  lcdPrint(" ​    ​GO",​ 1, 0); 
 + 
 +  // Reset the thumb master flag for players 
 +  for (int i = 0; i < NUMBER_OF_PLAYERS;​ i++) { 
 +    players[i].thumbMaster = false; 
 +  } 
 + 
 +  currentState = THUMB_MASTER_STATE;​ 
 + 
 +  // Send the message to the players 
 +  for (int i = 0; i < NUMBER_OF_PLAYERS;​ i++) { 
 +    sendMessage(i,​ MESSAGE_THUMB_MASTER,​ players[i].macAdress);​ 
 +  } 
 + 
 +  // Initialize the counter 
 +  thumbMasterCount = 0; 
 +
 + 
 +void sixChallenge() { 
 +  lcdPrint("​6 IS GUYS", 0, 1); 
 +  lcdPrint("​Boys Drink",​ 1, 0); 
 + 
 +  // Search for the boys 
 +  for (int i = 0; i < NUMBER_OF_PLAYERS;​ i++) { 
 +    if (players[i].gender == BOY) { 
 +      // Mark the player as drinker 
 +      players[i].shouldDrink = true; 
 + 
 +      // Send the message 
 +      sendMessage(i,​ MESSAGE_DRINK,​ players[i].macAdress);​ 
 +    } 
 +  } 
 +
 + 
 +void eightChallenge() { 
 +  lcdPrint("​8 IS MATE", 0, 1); 
 +  lcdPrint("​Choose Someone",​ 1, 0); 
 + 
 +  // Mark the player as drinker 
 +  players[currentTurn].shouldDrink = true; 
 +  // Send the message 
 +  sendMessage(currentTurn,​ MESSAGE_DRINK,​ players[currentTurn].macAdress);​ 
 + 
 +  // Change the state to choosing someone 
 +  currentState = CHOOSING_SOMEONE_STATE;​ 
 +
 + 
 +void nineChallenge() { 
 +  lcdPrint("​9 IS RHYME",​ 0, 1); 
 +  lcdPrint("​BEGIN",​ 1, 0); 
 + 
 +  delay(2000);​ 
 +   
 +  // Update the current state 
 +  currentState = COUNTDOWN_STATE;​ 
 +  currentToAnswer = currentTurn;​ 
 +  sendMessage(currentTurn,​ MESSAGE_SAY,​ players[currentTurn].macAdress);​ 
 + 
 +
 + 
 +void tenChallenge() { 
 +  lcdPrint("​10 IS CATEGORY",​ 0, 1); 
 +  lcdPrint("​BEGIN",​ 1, 0); 
 + 
 +  delay(2000);​ 
 + 
 +  // Update the current state 
 +  currentState = COUNTDOWN_STATE;​ 
 +  currentToAnswer = currentTurn;​ 
 +  sendMessage(currentTurn,​ MESSAGE_SAY,​ players[currentTurn].macAdress);​ 
 +
 + 
 +void JChallenge() { 
 +  lcdPrint("​J IS RULE", 0, 1); 
 +  lcdPrint("​Choose a Rule", 1, 0); 
 + 
 +
 + 
 +void QChallenge() { 
 +  lcdPrint("​Q IS QUESTION",​ 0, 1); 
 +
 + 
 +void KChallenge() { 
 +  lcdPrint("​K IS EVERYONE",​ 0, 1); 
 +  lcdPrint("​Everyone Drinks",​ 1, 0); 
 + 
 +  // Mark everyone as drinker 
 +  for (int i = 0; i < NUMBER_OF_PLAYERS;​ i++) { 
 +    // Mark the player as drinker 
 +    players[i].shouldDrink = true; 
 + 
 +    // Send the message 
 +    sendMessage(i,​ MESSAGE_DRINK,​ players[i].macAdress);​ 
 +  } 
 +
 + 
 +void AChallenge() { 
 +  lcdPrint("​A IS WATERFALL",​ 0, 1); 
 + 
 +  currentState = WATERFALL_STATE;​ 
 + 
 +  // Send the go message to the initiator 
 +  sendMessage(currentTurn,​ MESSAGE_WATERFALL_GO_INITIATOR,​ players[currentTurn].macAdress);​ 
 +  // Send the go message to all the players 
 +  for (int i = 0; i < NUMBER_OF_PLAYERS;​ i++) { 
 +    if (i == currentTurn) { 
 +      continue; 
 +    } 
 +    sendMessage(i,​ MESSAGE_WATERFALL_GO,​ players[i].macAdress);​ 
 +  } 
 +
 + 
 +void startRound(int card) { 
 +  // Update the game state 
 +  currentState = WAITING_FOR_DRINKS_STATE;​ 
 + 
 +  // Check the value of the card and proceed accordingly 
 +  switch (card) { 
 +    case 2: 
 +      twoChallenge();​ 
 +      break; 
 +    case 3: 
 +      threeChallenge();​ 
 +      break; 
 +    case 4: 
 +      fourChallenge();​ 
 +      break; 
 +    case 5: 
 +      fiveChallenge();​ 
 +      break; 
 +    case 6: 
 +      sixChallenge();​ 
 +      break; 
 +    case 8: 
 +      eightChallenge();​ 
 +      break; 
 +    case 9: 
 +      nineChallenge();​ 
 +      break; 
 +    case 10: 
 +      tenChallenge();​ 
 +      break; 
 +    case 11: 
 +      JChallenge();​ 
 +      break; 
 +    case 12: 
 +      QChallenge();​ 
 +      break; 
 +    case 13: 
 +      KChallenge();​ 
 +      break; 
 +    case 14: 
 +      AChallenge();​ 
 +      break; 
 +  } 
 +   
 +
 + 
 +int canMoveToNext() { 
 +  int ret = 1; 
 +  // Check the flag for each player 
 +  for (int i = 0; i < NUMBER_OF_PLAYERS;​ i++) { 
 +    // Check if the player drank 
 +    if (players[i].shouldDrink) { 
 +      // Send the alert message 
 +      sendMessage(i,​ MESSAGE_SCREAM,​ players[i].macAdress);​ 
 +      ret = 0; 
 +    } 
 +  } 
 + 
 +  // Everybody drank 
 +  return ret; 
 +
 + 
 +void setup() { 
 +  Serial.begin(9600);​ 
 + 
 +  // Initialize the players 
 +  players[0].macAdress[0] = 0xD4; 
 +  players[0].macAdress[1] = 0xD4; 
 +  players[0].macAdress[2] = 0xDA; 
 +  players[0].macAdress[3] = 0x5B; 
 +  players[0].macAdress[4] = 0x41; 
 +  players[0].macAdress[5] = 0x88; 
 +  players[0].gender = GIRL; 
 + 
 +  players[1].macAdress[0] = 0xD4; 
 +  players[1].macAdress[1] = 0xD4; 
 +  players[1].macAdress[2] = 0xDA; 
 +  players[1].macAdress[3] = 0x5A; 
 +  players[1].macAdress[4] = 0x5F; 
 +  players[1].macAdress[5] = 0x3C; 
 +  players[1].gender = BOY; 
 + 
 +  players[2].macAdress[0] = 0xD4; 
 +  players[2].macAdress[1] = 0xD4; 
 +  players[2].macAdress[2] = 0xDA; 
 +  players[2].macAdress[3] = 0x5A; 
 +  players[2].macAdress[4] = 0x66; 
 +  players[2].macAdress[5] = 0xEC; 
 +  players[2].gender = BOY; 
 + 
 +  // Initialize ESP NOW stuff 
 +  WiFi.mode(WIFI_STA);​ 
 +  if (esp_now_init() != ESP_OK) { 
 +    Serial.println("​Error initializing ESP NOW"​);​ 
 +    return; 
 +  } 
 + 
 +  esp_now_register_send_cb(OnDataSent);​ 
 +  esp_now_register_recv_cb(OnDataRecv);​ 
 + 
 + 
 +  // Register peers 
 +  peerInfo.channel = 0; 
 +  peerInfo.encrypt = false; 
 +  for (int i = 0; i < NUMBER_OF_PLAYERS;​ i++) { 
 +    memcpy(peerInfo.peer_addr,​ players[i].macAdress,​ 6); 
 +    if (esp_now_add_peer(&​peerInfo) != ESP_OK){ 
 +      Serial.println("​Failed to add peer"​);​ 
 +      return; 
 +    } 
 +  } 
 + 
 +  // LCD CODE 
 +  lcd.init();​ 
 +  lcd.clear(); ​         
 +  lcd.backlight();​ 
 +   
 +  // Print a message on both lines of the LCD. 
 +  lcd.setCursor(2,​0); ​  //Set cursor to character 2 on line 0 
 +  lcd.print("​King'​Cup!"​);​ 
 +   
 +  lcd.setCursor(2,​1); ​  //​Move cursor to character 2 on line 1 
 +  lcd.print("​Press NEXT"​);​ 
 +  //​--------------------------- 
 + 
 +  //BUTTON SETUP 
 +  pinMode(BUTTON_1_PIN,​ INPUT_PULLUP);​ 
 +  pinMode(BUTTON_2_PIN,​ INPUT_PULLUP);​ 
 +  pinMode(BUTTON_3_PIN,​ INPUT_PULLUP);​ 
 +  pinMode(BUTTON_NEXT_PIN,​ INPUT_PULLUP);​ 
 + 
 +  //​---------------------------- 
 + 
 +  // Initialize the card frequencies 
 +  for (int i = 2; i < 15; i++) { 
 +    cardFrequencies[i] = CARDS_PER_NUMBER;​ 
 +  ​
 +  // 7 card won't be played 
 +  cardFrequencies[7] = 0; 
 + 
 +  // Initialize the current state of the game 
 +  currentState = NEW_GAME_STATE;​ 
 +  currentTurn = 0; 
 +
 + 
 +void loop() { 
 +  if (currentState == NEW_GAME_STATE) { 
 +    // Check if the button was pressed 
 +    int nextButtonValue = digitalRead(BUTTON_NEXT_PIN);​ 
 +    if (!nextButtonValue) { 
 +      // Switch into the new card state 
 +      currentState = NEW_CARD_STATE;​ 
 +    } 
 +  } 
 + 
 +  if (currentState == NEW_CARD_STATE) { 
 +    // Check if game should be over 
 +    if (cardsLeft == 0) { 
 +      currentState = GAME_OVER_STATE;​ 
 +    } else { 
 +      lcdPrintAnimation();​ 
 +      // Generate a new card 
 +      int cardValue;​ 
 +      while (1) { 
 +        cardValue = random(2, 15); 
 +        if (cardFrequencies[cardValue] > 0) { 
 +          cardFrequencies[cardValue]--;​ 
 +          cardsLeft--;​ 
 +          break; 
 +        } 
 +      } 
 +      Serial.print("​Card Value: "); 
 +      Serial.println(cardValue);​ 
 + 
 +      // Start the new round depening on the card 
 +      startRound(cardValue);​ 
 +    } 
 +  } 
 + 
 +  if (currentState == WAITING_FOR_DRINKS_STATE) { 
 +    // Check the next button state 
 +    int nextButtonValue = digitalRead(BUTTON_NEXT_PIN);​ 
 +    if (!nextButtonValue) { 
 +      // Check if everybody is ready 
 +      if (canMoveToNext()) { 
 +        currentState = NEW_CARD_STATE;​ 
 +        currentTurn = (currentTurn + 1) % NUMBER_OF_PLAYERS;​ 
 +      } 
 +    } 
 +  } 
 + 
 +  if (currentState == CHOOSING_SOMEONE_STATE) { 
 +    // Check the input on the buttons 
 +    int button1Value = digitalRead(BUTTON_1_PIN);​ 
 +    int button2Value = digitalRead(BUTTON_2_PIN);​ 
 +    int button3Value = digitalRead(BUTTON_3_PIN);​ 
 + 
 +    if (!button1Value) { 
 +      players[PLAYER_1].shouldDrink = true; 
 +      sendMessage(PLAYER_1,​ MESSAGE_DRINK,​ players[PLAYER_1].macAdress);​ 
 +      // Update the current state 
 +      currentState = WAITING_FOR_DRINKS_STATE;​ 
 +    } else if (!button2Value) { 
 +      players[PLAYER_2].shouldDrink = true; 
 +      sendMessage(PLAYER_2,​ MESSAGE_DRINK,​ players[PLAYER_2].macAdress);​ 
 +      // Update the current state 
 +      currentState = WAITING_FOR_DRINKS_STATE;​ 
 +    } else if (!button3Value) { 
 +      players[PLAYER_3].shouldDrink = true; 
 +      sendMessage(PLAYER_3,​ MESSAGE_DRINK,​ players[PLAYER_3].macAdress);​ 
 +      // Update the current state 
 +      currentState = WAITING_FOR_DRINKS_STATE;​ 
 +    } 
 +  } 
 + 
 +  if (currentState == COUNTDOWN_STATE) { 
 +    lcd.clear();​ 
 +    a = millis(); 
 +    shouldRun = true; 
 +    while (shouldRun) { 
 +      c = millis(); 
 +      i = (c - a) / 1000; 
 +      if (shouldReset) { 
 +        a = millis(); 
 +        shouldReset = false; 
 +      } 
 +      if (i >= 5) { 
 +        shouldRun = false; 
 +      } 
 +      lcd.setCursor(5,​ 0); 
 +      lcd.print(i);​ 
 +      delay(100);​ 
 +    } 
 +    players[currentToAnswer].shouldDrink = true; 
 +    sendMessage(currentToAnswer,​ MESSAGE_DRINK,​ players[currentToAnswer].macAdress);​ 
 +    currentState = WAITING_FOR_DRINKS_STATE;​ 
 +  } 
 + 
 +  if (currentState == THUMB_MASTER_STATE) { 
 +    // Check if only one remains 
 +    if (thumbMasterCount == NUMBER_OF_PLAYERS - 1) { 
 +      // Check who it is 
 +      for (int i = 0; i < NUMBER_OF_PLAYERS;​ i++) { 
 +        if (!players[i].thumbMaster) { 
 +          players[i].shouldDrink = true; 
 +          sendMessage(i,​ MESSAGE_DRINK,​ players[i].macAdress);​ 
 +          break; 
 +        } 
 +      } 
 +      currentState = WAITING_FOR_DRINKS_STATE;​ 
 +    } 
 +    
 +  } 
 +   
 +  if (currentState == GAME_OVER_STATE) { 
 +    lcdPrint("​ GAME OVER", 0, 1); 
 +    currentState = IDLE_STATE;​ 
 +  } 
 +
 + 
 +</​code>​ 
 + 
 +==== Cod Player ==== 
 +Asemanator cu Masterul, Playerul este construi tot sub forma unui state-machine:​ 
 +<code cpp> 
 +#include <​esp_now.h>​ 
 +#include <​WiFi.h>​ 
 + 
 +#define THUMB_MASTER_BUTTON_PIN 18 
 +#define GENERAL_PURPOSE_BUTTON_PIN 19 
 +#define FORCE_SENSOR_PIN 36 
 +#define LED_PIN 22 
 +#define LED2_PIN 17 
 +#define BUZZER_PIN 23 
 + 
 +#define IDLE_STATE 0 
 +#define SHOULD_DRINK_STATE 1 
 +#define SHOULD_SAY_STATE 2 
 +#define THUMB_MASTER_STATE 3 
 +#define WATERFALL_STATE 4 
 + 
 +#define MESSAGE_DRINK 0 
 +#define MESSAGE_CONFIRM 1 
 +#define MESSAGE_SCREAM 2 
 +#define MESSAGE_SAY 3 
 +#define MESSAGE_GO_NEXT 4 
 +#define MESSAGE_THUMB_MASTER 5 
 +#define MESSAGE_WATERFALL_GO 6 
 +#define MESSAGE_WATERFALL_STOP 7 
 +#define MESSAGE_WATERFALL_GO_INITIATOR 8 
 + 
 + 
 +#define GLASS_NOT_LIFTED 0 
 +#define GLASS_LIFTED 1 
 +#define GLASS_BACK 2 
 + 
 +typedef struct message_t { 
 +  int sender; 
 +  int receiver; 
 +  int messageType;​ 
 +} message_t;​ 
 + 
 +// MAC Address of the master ESP 
 +uint8_t broadcastAddress[] = {0xE8, 0x31, 0xCD, 0x14, 0x21, 0x1C}; 
 + 
 +// The player ID 
 +int myId = -1; 
 + 
 +// Peer info structure 
 +esp_now_peer_info_t peerInfo; 
 + 
 +// The drinking flag 
 +int currentState;​ 
 + 
 +// The state of the buzzer 
 +bool buzzerOn = false; 
 + 
 +// The state of the glass 
 +int glassState;​ 
 + 
 +// Flag for the waterfall challenge 
 +bool canPutBack;​ 
 +bool isInitiator;​ 
 + 
 +const int movingAvgLength = 10; 
 +int movingAvgBuffer[movingAvgLength];​ 
 +int movingAvgIndex = 0; 
 + 
 +// Callback function executed when data is received 
 +void OnDataRecv(const uint8_t ​mac, const uint8_t *incomingData,​ int len) { 
 +  message_t message; 
 + 
 +  memcpy(&​message,​ incomingData,​ sizeof(message_t));​ 
 +  Serial.print("​Data received: "); 
 +  Serial.println(len);​ 
 +  Serial.print("​Sender:​ "); 
 +  Serial.println(message.sender);​ 
 +  Serial.print("​Message type: "); 
 +  Serial.println(message.messageType == 0 ? "​DRINK"​ : "​CONFIRM"​);​ 
 + 
 +  if (message.messageType == MESSAGE_DRINK) { 
 +    currentState = SHOULD_DRINK_STATE;​ 
 +    glassState = GLASS_NOT_LIFTED;​ 
 +  } 
 + 
 +  if (message.messageType == MESSAGE_SCREAM) { 
 +    buzzerOn = true; 
 +  } 
 + 
 +  if (message.messageType == MESSAGE_SAY) { 
 +    currentState = SHOULD_SAY_STATE;​ 
 +  } 
 + 
 +  if (message.messageType == MESSAGE_THUMB_MASTER) { 
 +    currentState = THUMB_MASTER_STATE;​ 
 +  } 
 + 
 +  if (message.messageType == MESSAGE_WATERFALL_GO) { 
 +    currentState = WATERFALL_STATE;​ 
 +    glassState = GLASS_NOT_LIFTED;​ 
 +    canPutBack = false; 
 +    isInitiator = false; 
 +  } 
 + 
 +  if (message.messageType == MESSAGE_WATERFALL_GO_INITIATOR) { 
 +    currentState = WATERFALL_STATE;​ 
 +    glassState = GLASS_NOT_LIFTED;​ 
 +    canPutBack = false; 
 +    isInitiator = true; 
 +  } 
 + 
 +  if (message.messageType == MESSAGE_WATERFALL_STOP) { 
 +    canPutBack = true; 
 +  } 
 + 
 +   // Check if it's the first message and update ID 
 +  if (myId == -1) { 
 +    myId = message.receiver;​ 
 +  } 
 + 
 +   
 +
 + 
 +// Callback when data is sent 
 +void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 
 +  char macStr[18];​ 
 +  Serial.print("​Packet to: "); 
 +  // Copies the sender mac address to a string 
 +  snprintf(macStr,​ sizeof(macStr),​ "​%02x:​%02x:​%02x:​%02x:​%02x:​%02x",​ 
 +           ​mac_addr[0],​ mac_addr[1],​ mac_addr[2],​ mac_addr[3], mac_addr[4],​ mac_addr[5])
 +  Serial.print(macStr);​ 
 +  Serial.print("​ send status:​\t"​);​ 
 +  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "​Delivery Success"​ : "​Delivery Fail"​);​ 
 +
 + 
 +int calculateMovingAverage(int sensorValue) { 
 +  movingAvgBuffer[movingAvgIndex] = sensorValue;​ 
 +  movingAvgIndex = (movingAvgIndex + 1) % movingAvgLength;​ 
 + 
 +  int sum = 0; 
 +  for (int = 0; i < movingAvgLength;​ i++) { 
 +    sum += movingAvgBuffer[i];​ 
 +  } 
 + 
 +  return sum / movingAvgLength;​ 
 +
 + 
 + 
 + 
 +void setup() { 
 +  // Initialize serial communication at 9600 bits per second: 
 +  Serial.begin(9600);​ 
 + 
 +  // Initialize ESP NOW stuff 
 +  // Set ESP32 as a Wi-Fi Station 
 +  WiFi.mode(WIFI_STA);​ 
 +  
 +  // Initilize ESP-NOW stuff 
 +  if (esp_now_init() != ESP_OK) { 
 +    Serial.println("​Error initializing ESP-NOW"​);​ 
 +    return; 
 +  } 
 +   
 +  // Register callback function 
 +  esp_now_register_recv_cb(OnDataRecv);​ 
 +  esp_now_register_send_cb(OnDataSent);​ 
 + 
 +  memcpy(peerInfo.peer_addr,​ broadcastAddress,​ 6); 
 +  if (esp_now_add_peer(&​peerInfo) != ESP_OK){ 
 +    Serial.println("​Failed to add peer"​);​ 
 +    return; 
 +  } 
 + 
 + 
 +  // Initialize the thumb master button 
 +  pinMode(THUMB_MASTER_BUTTON_PIN,​ INPUT_PULLUP);​ 
 +  pinMode(GENERAL_PURPOSE_BUTTON_PIN,​ INPUT_PULLUP);​ 
 +  // Initialize the led 
 +  pinMode(LED_PIN,​ OUTPUT); 
 +  pinMode(LED2_PIN,​ OUTPUT); 
 +  // Initialize the buzzer 
 +  pinMode(BUZZER_PIN,​ OUTPUT); 
 + 
 +  currentState = IDLE_STATE;​ 
 +
 + 
 +void loop() { 
 +  if (currentState == SHOULD_DRINK_STATE) { 
 +    // Turn on the LED 
 +    digitalWrite(LED_PIN,​ HIGH); 
 +    digitalWrite(LED2_PIN,​ LOW); 
 + 
 +    if (buzzerOn) { 
 +      digitalWrite(BUZZER_PIN,​ HIGH); 
 +    } 
 + 
 +    // Read the FSR value 
 +    int forceSensorValue = calculateMovingAverage(analogRead(FORCE_SENSOR_PIN));​ 
 + 
 +     
 +    if ((forceSensorValue <5) && glassState == GLASS_NOT_LIFTED) { 
 +      glassState = GLASS_LIFTED;​ 
 +    } 
 + 
 +    if ((forceSensorValue > 100) && glassState == GLASS_LIFTED) { 
 +      glassState = GLASS_BACK;​ 
 +    } 
 + 
 +    if (glassState == GLASS_BACK) { 
 +      // Send the confirmation message 
 +      // Create the message 
 +      message_t message = {myId, 3, MESSAGE_CONFIRM};​ 
 +   
 +      // Send the message 
 +      esp_err_t result = esp_now_send(broadcastAddress,​ (uint8_t *) &​message,​ sizeof(message_t));​ 
 + 
 +      // Check the result 
 +      if (result == ESP_OK) { 
 +        Serial.println("​Sent with success"​);​ 
 +        currentState = IDLE_STATE;​ 
 +        buzzerOn = false; 
 +      } 
 +      else { 
 +        Serial.println("​Error sending the data"​);​ 
 +      } 
 +    } 
 +  } 
 + 
 +  if (currentState == SHOULD_SAY_STATE) { 
 +    // Turn on the LED 
 +    digitalWrite(LED2_PIN,​ HIGH); 
 +     
 +    // Wait for the button press 
 +    int generalPurposeButtonState = digitalRead(GENERAL_PURPOSE_BUTTON_PIN);​ 
 + 
 +    if (!generalPurposeButtonState) { 
 +      // Send the message to master to go to the next player 
 +      message_t message = {myId, 3, MESSAGE_GO_NEXT};​ 
 +      esp_err_t result = esp_now_send(broadcastAddress,​ (uint8_t *) &​message,​ sizeof(message_t));​ 
 +      if (result == ESP_OK) { 
 +        Serial.println("​Sent with success"​);​ 
 +        currentState = IDLE_STATE;​ 
 +        buzzerOn = false; 
 +      } 
 +      else { 
 +        Serial.println("​Error sending the data"​);​ 
 +      } 
 +      digitalWrite(LED2_PIN,​ LOW); 
 +    } 
 +  } 
 + 
 +  if (currentState == THUMB_MASTER_STATE) { 
 +    int thumbMasterButtonState = digitalRead(THUMB_MASTER_BUTTON_PIN);​ 
 +    if (!thumbMasterButtonState) { 
 +      message_t message = {myId, 3, MESSAGE_THUMB_MASTER};​ 
 +      esp_err_t result = esp_now_send(broadcastAddress,​ (uint8_t *) &​message,​ sizeof(message_t));​ 
 +      if (result == ESP_OK) { 
 +        Serial.println("​Sent with success"​);​ 
 +        currentState = IDLE_STATE;​ 
 +        buzzerOn = false; 
 +      } 
 +      else { 
 +        Serial.println("​Error sending the data"​);​ 
 +      } 
 +    } 
 +  } 
 + 
 +  if (currentState == WATERFALL_STATE) { 
 +    int forceSensorValue = calculateMovingAverage(analogRead(FORCE_SENSOR_PIN));​ 
 +    if (forceSensorValue <5 && (glassState == GLASS_NOT_LIFTED || glassState == GLASS_BACK)) { 
 +      glassState = GLASS_LIFTED;​ 
 +    } 
 + 
 +    if (forceSensorValue > 5 && glassState == GLASS_LIFTED) { 
 +      glassState = GLASS_BACK;​ 
 +      if (isInitiator) { 
 +        canPutBack = true; 
 +      } 
 +    } 
 + 
 +    if ((glassState == GLASS_NOT_LIFTED || glassState == GLASS_BACK) && !canPutBack) { 
 +      // Turn on the alert 
 +      digitalWrite(LED_PIN,​ HIGH); 
 +      digitalWrite(BUZZER_PIN,​ HIGH); 
 +    } 
 + 
 +    if (glassState == GLASS_LIFTED && !canPutBack) { 
 +      digitalWrite(LED_PIN,​ LOW); 
 +      digitalWrite(BUZZER_PIN,​ LOW); 
 +    } 
 + 
 +    if (glassState == GLASS_BACK && canPutBack) { 
 +      message_t message = {myId, 3, MESSAGE_WATERFALL_STOP};​ 
 +      esp_err_t result = esp_now_send(broadcastAddress,​ (uint8_t *) &​message,​ sizeof(message_t));​ 
 +      if (result == ESP_OK) { 
 +        Serial.println("​Sent with success"​);​ 
 +        currentState = IDLE_STATE;​ 
 +        buzzerOn = false; 
 +      } 
 +      else { 
 +        Serial.println("​Error sending the data"​);​ 
 +      } 
 +    } 
 +  } 
 + 
 +  if (currentState == IDLE_STATE) { 
 +    // Turn off the LED 
 +    digitalWrite(LED_PIN,​ LOW); 
 +    digitalWrite(BUZZER_PIN,​ LOW); 
 +  } 
 +
 + 
 +</code>
  
 ===== Rezultate Obţinute ===== ===== Rezultate Obţinute =====
 +Modulul Master:
  
-<note tip> +{{:​pm:​prj2023:​adarmaz:​modulmasterkingspoza.jpeg?300|}}
-Care au fost rezultatele obţinute în urma realizării proiectului vostru. +
-</​note>​+
  
-===== Concluzii =====+Modul Player:
  
-===== Download =====+{{:​pm:​prj2023:​adarmaz:​modulplayerkingspoza.jpeg?​300|}}
  
-<note warning>​ +Video cu flow-ul jocului:
-O arhivă (sau mai multe dacă este cazul) ​cu fişierele obţinute în urma realizării proiectului:​ surse, scheme, etc. Un fişier README, un ChangeLog, un script de compilare şi copiere automată pe uC crează întotdeauna o impresie bună ;-).+
  
-Fişierele se încarcă pe wiki folosind facilitatea **Add Images or other files**. Namespace-ul în care se încarcă fişierele este de tipul **:pm:​prj20??:​c?​** sau **:​pm:​prj20??:​c?:​nume_student** (dacă este cazul)**Exemplu:​** Dumitru Alin, 331CC -> **:​pm:​prj2009:​cc:​dumitru_alin**. +<​html>​ 
-</note>+<iframe width="​560"​ height="​315"​ src="​https://www.youtube.com/​embed/​KnaIk49ovkk"​ title="​YouTube video player"​ frameborder="​0"​ allow="​accelerometer;​ autoplay; clipboard-write; encrypted-media;​ gyroscope; picture-in-picture;​ web-share"​ allowfullscreen></iframe></​html>​ 
 +===== Concluzii ===== 
 +Obiectivul meu in cadrul proiectului a fost sa gasesc o idee noua, si sa o implementez intr-un mod care sa ma invete ceva nou. Consider ca am atins ambele obiective. Nu am putut gasi pe internet ceva asemanator, ceea ce a fost in acelasi timp satisfacator si challenging,​ pentru ca nu am avut nicio varianta de fallback. Cel mai important lucru pe care l-am invatat este functionalitatea bibliotecii ESP-NOW.
  
 +Cel mai mare challenge are legatura cu faptul ca proiectul este alcatuit din multe module, ceea ce a facut testarea destul de dificila. Pe partea de cod, implementarea initiala a comunicarii si a unei provocari de test a fost cea mai interesanta,​ urmand ca restul de functionalitati sa fie usor de implementat,​ fiind foarte asemanatoare cu prima.
 +
 +===== Download =====
 +Link Github: https://​github.com/​mihneabranzeu/​King-s-Cup-Arduino
 ===== Jurnal ===== ===== Jurnal =====
   * 5 Mai - Am creat pagina de documentatie   * 5 Mai - Am creat pagina de documentatie
 +  * 6 Mai - Am comandat piesele
 +  * 10 Mai - Au ajuns piesele
 +  * 20 - 23 Mai - Am lucrat la proiect
 +  * 24 Mai - Am prezentat proiectul la laborator
 +  * 30 Mai - PM Fair
 ===== Bibliografie/​Resurse ===== ===== Bibliografie/​Resurse =====
- +  ​https://www.youtube.com/watch?v=bEKjCDDUPaU 
-<​note>​ +  * https://dronebotworkshop.com/​esp-now/​
-Listă cu documente, datasheet-uri,​ resurse Internet folosite, eventual grupate pe **Resurse Software** şi **Resurse Hardware**. +
-</note> +
- +
-<​html><​a class="​media mediafile mf_pdf"​ href="?do=export_pdf">​Export to PDF</a></html>+
  
pm/prj2023/adarmaz/kings-cup.1685404569.txt.gz · Last modified: 2023/05/30 02:56 by mihnea.branzeu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0