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:27]
mihnea.branzeu [Design Modul Master]
pm:prj2023:adarmaz:kings-cup [2023/05/30 09:24] (current)
mihnea.branzeu [Rezultate Obţinute]
Line 19: Line 19:
   * **Q** - Pana la final, cei care iti raspund la orice intrebare trebuie sa bea o gura.   * **Q** - Pana la final, cei care iti raspund la orice intrebare trebuie sa bea o gura.
   * **K** - Toti beau o gura.   * **K** - Toti beau o gura.
-In cazul proiectului meu, pachetul de carti va fi inlocuit de un ecran LCD, care va afisa direct, cu ajutorul unui algoritm, provocarea pentru fiecare jucator.+  * **A** - Cascada: Provocare speciala unde toata lumea bea, cu conditia ca un jucator nu se poate opri inainte jucatorului din stanga lui. 
 +In cazul proiectului meu, pachetul de carti va fi inlocuit de un ecran LCD, care va afisa direct, cu ajutorul unui algoritm ​de simulare a unui pachet de carti, provocarea pentru fiecare jucator.
  
 ===== Descriere generală ===== ===== Descriere generală =====
Line 26: Line 27:
   - Mai multe module identice, alcatuite din ESP32, butoane, LEDuri si LCDuri, prin care jucatorii vor putea juca jocul   - Mai multe module identice, alcatuite din ESP32, butoane, LEDuri si LCDuri, prin care jucatorii vor putea juca jocul
  
-{{pm:​prj2023:​adarmaz:​screenshot_2023-05-05_at_18.29.48.png?​800x300}}+{{pm:​prj2023:​adarmaz: ​schemakings.png?​800x300}}
  
 Initial, modulul master va fi configurat, fiind specificate informatii despre jucatori. Aceste informatii vor fi trimis apoi catre modulele secundare, prin Wi-Fi, cu ajutorul ESPNow. \\ Initial, modulul master va fi configurat, fiind specificate informatii despre jucatori. Aceste informatii vor fi trimis apoi catre modulele secundare, prin Wi-Fi, cu ajutorul ESPNow. \\
-Atunci cand jocul incepe, masterul va selecta random o provocare pe care o va afisa pe LCD-ul sau. Jucatorii care vor trebui sa execute provocarea vor fi anuntati cu ajutorul LEDurilor de pe modulele lor. Jocul nu va putea fi continuat pana ce toti executa provocarea, fapt ce poate fi determinat cu ajutorul senzorului de forta, care va sta sub paharul fiecaruia, ​si care va detecta daca o cantite threshold a fost consumata in urma provocarii.+Atunci cand jocul incepe, masterul va selecta random o provocare pe care o va afisa pe LCD-ul sau. Jucatorii care vor trebui sa execute provocarea vor fi anuntati cu ajutorul LEDurilor de pe modulele lor. Jocul nu va putea fi continuat pana ce toti executa provocarea, fapt ce poate fi determinat cu ajutorul senzorului de forta, care va sta sub paharul fiecaruia. In cazul in care se incearca trecerea la provocarea urmatoare fara ca toti sa execute provocareabuzzerul de pe modulele celor care incearca sa triseze ​va incepe sa sune, oprindu-se doar atunci cand jucatorul executa provocarea.
 ===== Hardware Design ===== ===== Hardware Design =====
 Proiectul este alcatuit din 4 componente distincte, dintre care una este modulul master, care controleaza flow-ul jocului, iar celelalte trei sunt module player, construite identic, cate unul pentru fiecare jucator. Proiectul este alcatuit din 4 componente distincte, dintre care una este modulul master, care controleaza flow-ul jocului, iar celelalte trei sunt module player, construite identic, cate unul pentru fiecare jucator.
Line 36: Line 37:
  
 Schema electrica a modulului: Schema electrica a modulului:
-{{pm:​prj2023:​adarmaz:​screenshot_2023-05-05_at_18.29.48.png?​800x300}} 
- 
  
 +{{pm:​prj2023:​adarmaz:​ mastermodulekings.png?​500x300}}
  
 Componentele pe care le-am utilizat sunt: Componentele pe care le-am utilizat sunt:
-  * ESP32 (4x) +  * ESP32-WROOM-32D 
-  * ecran LCD +  * Display ​LCD 16x2 I2C 
-  * butoane+  * 4xButoane
   * LEDuri   * LEDuri
-  * senzor forta (3x) + 
-  * fire +==== Design Modul Player ==== 
-  * baterii 9V+Acest tip de modul este alcatuit dintr-un ESP32, 2 butoane, 2 LEDuri, un buzzer si un senzor ​de forta. Butoanele sunt utilizate pentru in timpul provocarilor,​ iar LEDurile au rolul de a semnala diferite evenimente pe parcursul jocului, precum randul unui anume jucator, sau faptul ca acesta trebuie sa bea. Senzorul de forta, plasat sub paharul jucatorului,​ are rolul de a detecta atunci cand acesta bea, pentru a exclude posibilele tentative de a trisa. Buzzerul anunta jucatorii care nu si-au indeplinit provocarea si incearca sa ascunda acest lucru. 
 + 
 +Schema electrica a modulului:​ 
 + 
 +{{pm:​prj2023:​adarmaz:​ playermoduleking.png?​500x350}} 
 + 
 +Componentele pe care le-am utilizat sunt: 
 +  * ESP32-WROOM-32D 
 +  * 2xLED 
 +  * 2xButoane 
 +  * Force Resisting Sensor 
 +  * Buzzer
  
 ===== 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.1685402856.txt.gz · Last modified: 2023/05/30 02:27 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