Project tutorial
DM Console (D&D)

DM Console (D&D)

When your first Arduino project is to build a moon lander, or more exactly a genuine working product.

  • 213 views
  • 0 comments
  • 1 respect

Components and supplies

Necessary tools and machines

Lasercutter
Laser cutter (generic)
3drag
3D Printer (generic)
09507 01
Soldering iron (generic)
4966285
Solder Wire, Lead Free

About this project

For my first Arduino project, I decided to make a DM console as a birthday present to a dear childhood friend.

For those who wish to see the final product us this link.

It started with a sketch of the functions and looks:

An LCD screen in the middle and around 13 buttons around it. 1 button for D20 roll, 1 button for random gold treasure, 5 buttons for generating random text from a file (magic item, monster, NPC, Spell, DM decision), 6 buttons for background music.

I had most of the hardware I needed from a project that never began. So first I downloaded the Arduino Desktop IDE. My computer didn’t recognize the Arduino – needed drivers for the Chinese chip. Now I could start.

Very fast I understood I need an SD card for the files and one for the music. Done.

Step 1: Connect an LCD screen and print to it.

That was easy using this link. Got a bigger screen and used the code from step 1 here to figure the LCD proper address. With that I wrote the following code:

//YWROBOT
//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F,20,4);  // set the LCD address to 0x3F for a 20 chars and 4 line display
int i=0;
int n=0;
void setup()
{
 Serial.begin(9600);
 lcd.init();                      // initialize the lcd 
 // Print a message to the LCD.
 lcd.backlight();
 lcd.setCursor(1,0);
 lcd.print("Hello, world!");
 lcd.setCursor(0,1);
 lcd.print("Ywrobot Arduino!");
}
void loop()
{
 delay(1000);
 lcd.clear();
 n = i%7;
 if (n < 1)
 {
 lcd.setCursor(1,0);
 lcd.print("BOOOOM!!");
 Serial.println("BOOM!"); 
 }
 else 
 {
   Serial.println(i%7);
   lcd.setCursor(0,1);
   lcd.print(i);
 }
 i++;
} 

Step 2: Generating a random number.

Using the Random function I wrote a test code – generating random numbers between 1 to 20 on the screen with text above:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
int i=0;
int n=0;
void setup()
{
 Serial.begin(9600);
 lcd.init();                      // initialize the lcd 
 // Print a message to the LCD.
 lcd.backlight();
 lcd.setCursor(1,0);
 lcd.print("Hello, world!");
 lcd.setCursor(0,1);
 lcd.print("Lets roll some D20s!");
}
void loop()
{
 delay(1000);
 lcd.clear();
 lcd.setCursor(1,0);
 lcd.print("5 X D20 roll");
 lcd.setCursor(0,1);
 lcd.print(random(1, 21));
 lcd.setCursor(3,1);
 lcd.print(random(1, 21));
 lcd.setCursor(6,1);
 lcd.print(random(1, 21));
 lcd.setCursor(9,1);
 lcd.print(random(1, 21));
 lcd.setCursor(12,1);
 lcd.print(random(1, 21));
} 

Step 3: Connect and code a button to work.

I learned how to do that using this link.

And that’s how the code for the D20 was born:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F,20,4);  // set the LCD address to 0x3F for a 20
int i=0;
int n=0;
// constants won't change. They're used here to set pin numbers:
const int BUTTON_PIN_D20 = 7; // the number of the pushbutton pin
// this code shows on LCD 5 random D20 result when pushing a buuton!
// Variables will change:
int currentStateD20;    // the current reading from the input pin
void setup()
{
 // initialize serial communication at 9600 bits per second:
 Serial.begin(9600);
 lcd.init();                      // initialize the lcd 
 // Print a message to the LCD.
 lcd.backlight();
 lcd.setCursor(0,2);
 lcd.print("Hello, DM all mighty");
 // initialize the pushbutton pin as an pull-up input
 // the pull-up input pin will be HIGH when the switch is open and LOW when the switch is closed.
 pinMode(BUTTON_PIN_D20, INPUT_PULLUP);
}
void loop()
{
 // read the state of the switch/button:
 currentStateD20 = digitalRead(BUTTON_PIN_D20);
 if(currentStateD20 == LOW) 
 {
 lcd.clear();
 lcd.setCursor(1,0);
 lcd.print("5 X D20 roll");
 lcd.setCursor(0,2);
 lcd.print(random(1, 21));
 lcd.setCursor(3,2);
 lcd.print(random(1, 21));
 lcd.setCursor(6,2);
 lcd.print(random(1, 21));
 lcd.setCursor(9,2);
 lcd.print(random(1, 21));
 lcd.setCursor(12,2);
 lcd.print(random(1, 21));
  //delay(1000);
 }
}

Step 4: Reading a specific line from an SD card.

First I learned to write and read to/from an SDcard. But that’s how to read the entire file, I need only a specific line! Luckily I found this post and used @septillion code:

voidprintLineN(unsigned int lineNumer){
 myFile.seek(0);
 char cr;
 for(unsigned int i = 0; i < (lineNumber -1);){
   cr = MyFile.read()
 if(cr == '\n'){
     i++;
   }
 }
 //Now we areat the right line
 while(true){
   cr = myFile.read();
   Serial.write(cr);
 if(cr == '\n'){
 break;
   }
 }
 //a for loopwith a read limit might be a good idea
}

I must admit that I didn’t understand the code at first and was afraid to use it. Searched for other solutions (even thought of building a server to communicate with an Arduino with WIFI), eventually had the courage to play with this code and use it for my project – it works like magic.

Another friend (Ofir Sofer) helped me with the content for the TXT files and I was on my way to conquering this segment. I built the following function, it works fine but has a little bug – an unwanted finishing char.

After many tries I found the solution to this bug by adding this code line:

//closing the string without \n\r which adds unwanted char in the end
rnd_string[j-2]= 0;

and the entire function code:

// this function finds the needed line (lineNumber) in a txt file from 
//sd card (indecated by filenmber) and shows it on the lcd screen
void printLineN(unsigned int lineNumber, unsigned int filenumber ){
 char rnd_string[LCD_colums+1] = ""; 
 rnd_string[1] = 0; 
 char cr;
 //  SD card initialization
 while (!Serial) {
 ; // wait for serial port to connect. Needed for native USB port only
 }
 Serial.print(F("Initializing SD card..."));
 if (!SD.begin(10)) {
   Serial.println(F("initialization failed!"));
   while (1);
 }
 Serial.println(F("initialization done."));
 // finding the correct file to open
 switch (filenumber) {
   case 1: {myFile = SD.open("monsters.txt");
           break;}
   case 2: {myFile = SD.open("spells.txt");
           break;}
   case 3: {myFile = SD.open("DMD.txt");
           break;}
   case 4: {myFile = SD.open("item.txt");
           break;}
   case 5: {myFile = SD.open("NPCtrait.txt");
           break;}
 }
 myFile.seek(0);
 Serial.println(F("file opened sucssesfuly."));  
 // seeking the right line in the file
 for(unsigned int i = 0; i < (lineNumber -1);){
   cr = myFile.read();
   if(cr == '\n'){
     i++;
   }
 }
 int j=0; //string index
 cr = ' '; // initialization of cr to get inside while loop
 //Now we are at the right line
 while(cr != '\n'){ 
   cr = myFile.read();
   Serial.println(cr);
   rnd_string[j] = cr;
   j++;
   //if string is bigger than LCD max columns than print to LCD 
   //and move one row down
   if (j == LCD_colums) { 
     if (rnd_string[j] == '\n') 
        rnd_string[j-1] = 0;
     else if (rnd_string[j] == '\r') 
        rnd_string[j-1] = 0;
     lcd.setCursor(0,1);
     //print first row
     lcd.print(rnd_string);
     // zero the string and the counter
     rnd_string[0] = 0;
     j=0;
   }
 }
     //closing the string without \n\r which adds unwanted char in the end
     rnd_string[j-2] = 0;
     lcd.setCursor(0,2);
     //printing the string to LCD
     lcd.print(rnd_string);
     // close the file:
     myFile.close();
}

Step 5: FAILING to Connect and use an MP3 player module

I had this part “HW-195-JL”:

It’s a useful part – has a really strong amplifier, works as a standalone – so all I need is to figure out how to send commands to it and continue with the Arduino code. But it has only 3 controls! Back | Play | FW. And I wish it to play a specific file when a button is triggered. I decided to try using the FW to skip the needed files and get to the one I wish to be played. The coding felt easy. The connection to the Arduino was the big challenge!

Step5.1: Soldering and understanding the mp3 player

Used a capacitor at the power connection to reduce static noises.

The Audio out is mono, I connected it to a 3.5mm female jack and connected the RNG to TIPto get a semi-stereo output:

Step5.2: Controlling the mp3 module via Arduino commends.

This almost broke me. The triggers worked only with 3v signals. No matter what I tried I couldn't find a way to send such a signal from the Arduino.

Then it hit me! I need to use transistors to control the mp3 module.

Got myself3 2n2222 transistors connected to E to GND, B to Arduino pin, C to the other connection, and tried it out. Didn’t work ☹. After some googling, I understood I needed a 1KΩ resistor (and to replace the 2n2222 I probably burned with 5V). Finally, it worked!

[Then I decided to solder everything better and it didn’t work anymore – I ditched this part and bought another module]

Step5.3: Writing the code.

After all, I learned so far, the code for this part felt really easy.

So, I wrote the code and got awful skipping sounds as the loop code started running. After a long hour of checking the code part by part and the hardware connection, I figured that PIN 3 in the Arduino was the cause for it and moved one button to another pin. It worked! Until it didn’t work ☹

const int transistor_back = 6;      // back connected to digital pin 6
const int transistor_play = 4;      // back connected to digital pin 4
const int transistor_FW = 5;      // back connected to digital pin 5
const int BUTTON_PIN_city = 7;    // activation city button connected to digital pin 7
const int BUTTON_PIN_tavern = 12;    // activation tavern button connected to digital pin 12
const int BUTTON_PIN_nature = 11;    // activation nature button connected to digital pin 11
const int BUTTON_PIN_rain = 10;    // activation rain button connected to digital pin 10
const int BUTTON_PIN_battle = 9;    // activation battle button connected to digital pin 9
const int BUTTON_PIN_general = 8;    // activation general button connected to digital pin 8
int currentState1 = 0;
int currentState2 = 0;
int currentState3 = 0;
int currentState4 = 0;
int currentState5 = 0;
int currentState6 = 0;
int currentSong = 1;
void setup() {
   // initialize serial communication at 9600 bits per second:
 Serial.begin(9600);
 // initialize the pushbutton pin as an pull-up input
 // the pull-up input pin will be HIGH when the switch is open and LOW when the switch is closed.
 pinMode(BUTTON_PIN_city, INPUT_PULLUP);
 pinMode(BUTTON_PIN_tavern, INPUT_PULLUP);
 pinMode(BUTTON_PIN_nature, INPUT_PULLUP);
 pinMode(BUTTON_PIN_rain, INPUT_PULLUP);
 pinMode(BUTTON_PIN_battle, INPUT_PULLUP);
 pinMode(BUTTON_PIN_general, INPUT_PULLUP);
 pinMode(transistor_back, OUTPUT);  // sets the transistor pin as output
 pinMode(transistor_play, OUTPUT);  // sets the transistor pin as output
 pinMode(transistor_FW, OUTPUT);  // sets the transistor pin as output
 digitalWrite (transistor_back, LOW);
 digitalWrite (transistor_FW, LOW);
 Serial.println("playing the greetings sound");
 delay(19000);
 // at this moment I returned to the point that I'm unable to send the right massage to the MP3 player
 digitalWrite (transistor_play, HIGH);
 delay(100);
 digitalWrite (transistor_play, LOW);
 Serial.println("done play");
 }
void FW(int k) {
 Serial.println("start FW");
 if (k < 0) k = k+7;
 for (int i = 0 ; i < k ; i++){
   digitalWrite (transistor_FW, HIGH);
   delay(100);
   digitalWrite (transistor_FW, LOW);
   delay(100);
 }
}
void back(int k) {
 Serial.println("start back");
 if (k < 0) k = k+7;
 for (int i = 0 ; i < k ; i++){
   digitalWrite (transistor_back, HIGH);
   delay(100);
   digitalWrite (transistor_back, LOW);
   delay(100);
 }
}
void play() {
 digitalWrite (transistor_play, HIGH);
 delay(100);
 digitalWrite (transistor_play, LOW);
}
void loop() {
  Serial.println("start loop");
  //read city button
  currentState1 = digitalRead(BUTTON_PIN_city);
  if(currentState1 == LOW) 
  { 
   if (currentSong == 2) play();
   FW(2 - currentSong);
   currentSong = 2;
  }
  //read tavern button
  currentState2 = digitalRead(BUTTON_PIN_tavern);
  if(currentState2 == LOW) 
  { 
   if (currentSong == 3) play();
   FW(3 - currentSong);
   currentSong = 3;
  }
  //read Nature button
  currentState3 = digitalRead(BUTTON_PIN_nature);
  if(currentState3 == LOW) 
  { 
   if (currentSong == 4) play();
   FW(4 - currentSong);
   currentSong = 4;
  }
  //read Rain button
  currentState4 = digitalRead(BUTTON_PIN_rain);
  if(currentState4 == LOW) 
  { 
   if (currentSong == 5) play();
   FW(5 - currentSong);
   currentSong = 5;
  }
  //read Battle button
  currentState5 = digitalRead(BUTTON_PIN_battle);
  if(currentState5 == LOW) 
  { 
   if (currentSong == 6) play();
   FW(6 - currentSong);
   currentSong = 6;
  }
  //read General button
  currentState6 = digitalRead(BUTTON_PIN_general);
  if(currentState6 == LOW) 
  { 
   if (currentSong == 7) play();
   FW(7 - currentSong);
   currentSong = 7;
  }
}

Step 5 all over again – MP3 player from scratch

I bought another module - YX6300 UART TTL serial MP3 music player module

This module is much simpler but lacks the amplifier.

Found this post which made everything simple. I wrote the code in no time and the only challenge was figuring how the module sorted the files – by date created.

This module has the ability to play a specific file and much more all by communication between the Arduino and the module (the communication functions are in the code from the post I linked).

Finale MP3code:

// COPYIED FROM: https://create.arduino.cc/projecthub/moreirarobotics/how-to-use-the-yx5300-mp3-module-catalex-with-arduino-171a23
// Edited by Dror Moshe Aharoni
// pins connection: 7-12 - music buttons, 5 to mp3 TX, 6 to MP3 RX
#include <SoftwareSerial.h>
#define ARDUINO_RX 5  //should connect to TX of the Serial MP3 Player module
#define ARDUINO_TX 6  //connect to RX of the module
SoftwareSerial mp3(ARDUINO_RX, ARDUINO_TX);
//#define mp3 Serial3    // Connect the MP3 Serial Player to the Arduino MEGA Serial3 (14 TX3 -> RX, 15 RX3 -> TX)
static int8_t Send_buf[8] = {0}; // Buffer for Send commands.  
static uint8_t ansbuf[10] = {0}; // Buffer for the answers.    
String mp3Answer;           // Answer from the MP3.
String sanswer(void);
String sbyte2hex(uint8_t b);
int BUTTON_PIN;
int last_pushed;
int buttonState;
int currentSong = 1;
int pause = 0;
/************ Command byte **************************/
#define CMD_NEXT_SONG     0X01  // Play next song.
#define CMD_PREV_SONG     0X02  // Play previous song.
#define CMD_PLAY_W_INDEX  0X03
#define CMD_VOLUME_UP     0X04
#define CMD_VOLUME_DOWN   0X05
#define CMD_SET_VOLUME    0X06
#define CMD_SNG_CYCL_PLAY 0X08  // Single Cycle Play.
#define CMD_SEL_DEV       0X09
#define CMD_SLEEP_MODE    0X0A
#define CMD_WAKE_UP       0X0B
#define CMD_RESET         0X0C
#define CMD_PLAY          0X0D
#define CMD_PAUSE         0X0E
#define CMD_PLAY_FOLDER_FILE 0X0F
#define CMD_STOP_PLAY     0X16
#define CMD_FOLDER_CYCLE  0X17
#define CMD_SHUFFLE_PLAY  0x18 //
#define CMD_SET_SNGL_CYCL 0X19 // Set single cycle.
#define CMD_SET_DAC 0X1A
#define DAC_ON  0X00
#define DAC_OFF 0X01
#define CMD_PLAY_W_VOL    0X22
#define CMD_PLAYING_N     0x4C
#define CMD_QUERY_STATUS      0x42
#define CMD_QUERY_VOLUME      0x43
#define CMD_QUERY_FLDR_TRACKS 0x4e
#define CMD_QUERY_TOT_TRACKS  0x48
#define CMD_QUERY_FLDR_COUNT  0x4f
/************ Opitons **************************/
#define DEV_TF            0X02
/*********************************************************************/
void setup()
{
 // starting the serial communication through the mp3 object
 // The mp3 module is controlled through commands received by the Arduino serial. In this process, we used the SoftwareSerial library and emulated a serial on the Arduino digital pins.
 // Thus, you will be able to use the Arduino to control the MP3 module through commands sent to it.
 Serial.begin(9600);
 mp3.begin(9600);
 delay(500);
 // initialization of the MP3 Card module
 sendCommand(CMD_SEL_DEV, 0, DEV_TF);
 delay(500);
 // initialize the pushbutton pins as an pull-up input
 // the pull-up input pin will be HIGH when the switch is open and LOW when the switch is closed.
 for (BUTTON_PIN = 7; BUTTON_PIN < 13; BUTTON_PIN++){
   //BUTTON_PIN is the number of the pushbutton pin
   pinMode(BUTTON_PIN, INPUT_PULLUP); 
 }
 sendCommand(0x06, 0 , 30);
 Serial.println("playing the greetings sound");
 sendCommand(0x03, 0 , 1);
 //delay(19000);
 delay(1000);
 last_pushed = 1;
}
//starting loop
void loop() {
 //Serial.println("start loop");
 buttonState = HIGH;
 BUTTON_PIN = 6;
 // read the state of the switch/button:
 while (buttonState == HIGH){
   BUTTON_PIN++;
   if (BUTTON_PIN == 13) BUTTON_PIN = 7;
   buttonState = digitalRead(BUTTON_PIN);
   // print out the button's state
   //Serial.println(BUTTON_PIN);
   //Serial.println(buttonState);
}
//
delay(500); 
//Serial.println("DM pushed button:");
 //Serial.println(BUTTON_PIN);
 if (BUTTON_PIN == last_pushed){
   switch (pause) {
     case 0:
       sendCommand(0x0E);
       last_pushed = BUTTON_PIN;
       pause = 1;
       sanswer();
       break;
     case 1:
       pause = 0;
       sendCommand(0x03, 0, BUTTON_PIN-5);
       if (mp3.available()) Serial.println(decodeMP3Answer());
       break;
   }
 }
 else 
 {
   // saving the last pushed button in order to enable music pause
   last_pushed = BUTTON_PIN;
   // playing the desired music file
   sendCommand(0x03, 0, BUTTON_PIN-5);
   //delay(3000);
   // Check for the answer.
 if (mp3.available()) Serial.println(decodeMP3Answer());
 }
}
/********************************************************************************/
/*Function sendMP3Command: seek for a 'c' command and send it to MP3  */
/*Parameter: c. Code for the MP3 Command, 'h' for help.                                                                                                         */
/*Return:  void                                                                */
void sendMP3Command(char c) {
 switch (c) {
   case '?':
   case 'h':
     Serial.println("HELP  ");
     Serial.println(" p = Play");
     Serial.println(" P = Pause");
     Serial.println(" > = Next");
     Serial.println(" < = Previous");
     Serial.println(" s = Stop Play"); 
     Serial.println(" + = Volume UP");
     Serial.println(" - = Volume DOWN");
     Serial.println(" c = Query current file");
     Serial.println(" q = Query status");
     Serial.println(" v = Query volume");
     Serial.println(" x = Query folder count");
     Serial.println(" t = Query total file count");
     Serial.println(" f = Play folder 1.");
     Serial.println(" S = Sleep");
     Serial.println(" W = Wake up");
     Serial.println(" r = Reset");
     break;
   case 'p':
     Serial.println("Play ");
     sendCommand(CMD_PLAY);
     break;
   case 'P':
     Serial.println("Pause");
     sendCommand(CMD_PAUSE);
     break;
   case '>':
     Serial.println("Next");
     sendCommand(CMD_NEXT_SONG);
     sendCommand(CMD_PLAYING_N); // ask for the number of file is playing
     break;
   case '<':
     Serial.println("Previous");
     sendCommand(CMD_PREV_SONG);
     sendCommand(CMD_PLAYING_N); // ask for the number of file is playing
     break;
   case 's':
     Serial.println("Stop Play");
     sendCommand(CMD_STOP_PLAY);
     break;
   case '+':
     Serial.println("Volume Up");
     sendCommand(CMD_VOLUME_UP);
     break;
   case '-':
     Serial.println("Volume Down");
     sendCommand(CMD_VOLUME_DOWN);
     break;
   case 'c':
     Serial.println("Query current file");
     sendCommand(CMD_PLAYING_N);
     break;
   case 'q':
     Serial.println("Query status");
     sendCommand(CMD_QUERY_STATUS);
     break;
   case 'v':
     Serial.println("Query volume");
     sendCommand(CMD_QUERY_VOLUME);
     break;
   case 'x':
     Serial.println("Query folder count");
     sendCommand(CMD_QUERY_FLDR_COUNT);
     break;
   case 't':
     Serial.println("Query total file count");
     sendCommand(CMD_QUERY_TOT_TRACKS);
     break;
   case 'f':
     Serial.println("Playing folder 1");
     sendCommand(CMD_FOLDER_CYCLE, 1, 0);
     break;
   case 'S':
     Serial.println("Sleep");
     sendCommand(CMD_SLEEP_MODE);
     break;
   case 'W':
     Serial.println("Wake up");
     sendCommand(CMD_WAKE_UP);
     break;
   case 'r':
     Serial.println("Reset");
     sendCommand(CMD_RESET);
     break;
 }
}
/********************************************************************************/
/*Function decodeMP3Answer: Decode MP3 answer.                                  */
/*Parameter:-void                                                               */
/*Return: The                                                  */
String decodeMP3Answer() {
 String decodedMP3Answer = "";
 decodedMP3Answer += sanswer();
 switch (ansbuf[3]) {
   case 0x3A:
     decodedMP3Answer += " -> Memory card inserted.";
     break;
   case 0x3D:
     decodedMP3Answer += " -> Completed play num " + String(ansbuf[6], DEC);
     //sendCommand(CMD_NEXT_SONG);
     //sendCommand(CMD_PLAYING_N); // ask for the number of file is playing
     break;
   case 0x40:
     decodedMP3Answer += " -> Error";
     break;
   case 0x41:
     decodedMP3Answer += " -> Data recived correctly. ";
     break;
   case 0x42:
     decodedMP3Answer += " -> Status playing: " + String(ansbuf[6], DEC);
     break;
   case 0x48:
     decodedMP3Answer += " -> File count: " + String(ansbuf[6], DEC);
     break;
   case 0x4C:
     decodedMP3Answer += " -> Playing: " + String(ansbuf[6], DEC);
     break;
   case 0x4E:
     decodedMP3Answer += " -> Folder file count: " + String(ansbuf[6], DEC);
     break;
   case 0x4F:
     decodedMP3Answer += " -> Folder count: " + String(ansbuf[6], DEC);
     break;
 }
 return decodedMP3Answer;
}
/********************************************************************************/
/*Function: Send command to the MP3                                             */
/*Parameter: byte command                                                       */
/*Parameter: byte dat1 parameter for the command                                */
/*Parameter: byte dat2 parameter for the command                                */
void sendCommand(byte command){
 sendCommand(command, 0, 0);
}
void sendCommand(byte command, byte dat1, byte dat2){
 delay(20);
 Send_buf[0] = 0x7E;    //
 Send_buf[1] = 0xFF;    //
 Send_buf[2] = 0x06;    // Len
 Send_buf[3] = command; //
 Send_buf[4] = 0x01;    // 0x00 NO, 0x01 feedback
 Send_buf[5] = dat1;    // datah
 Send_buf[6] = dat2;    // datal
 Send_buf[7] = 0xEF;    //
 Serial.print("Sending: ");
 for (uint8_t i = 0; i < 8; i++)
 {
   mp3.write(Send_buf[i]) ;
   Serial.print(sbyte2hex(Send_buf[i]));
 }
 Serial.println();
}
/********************************************************************************/
/*Function: sbyte2hex. Returns a byte data in HEX format.                       */
/*Parameter:- uint8_t b. Byte to convert to HEX.                                */
/*Return: String                                                                */
String sbyte2hex(uint8_t b)
{
 String shex;
 shex = "0X";
 if (b < 16) shex += "0";
 shex += String(b, HEX);
 shex += " ";
 return shex;
}
/********************************************************************************/
/*Function: shex2int. Returns a int from an HEX string.                         */
/*Parameter: s. char *s to convert to HEX.                                      */
/*Parameter: n. char *s' length.                                                */
/*Return: int                                                                   */
int shex2int(char *s, int n){
 int r = 0;
 for (int i=0; i<n; i++){
    if(s[i]>='0' && s[i]<='9'){
     r *= 16; 
     r +=s[i]-'0';
    }else if(s[i]>='A' && s[i]<='F'){
     r *= 16;
     r += (s[i] - 'A') + 10;
    }
 }
 return r;
}
/********************************************************************************/
/*Function: sanswer. Returns a String answer from mp3 UART module.          */
/*Parameter:- uint8_t b. void.                                                  */
/*Return: String. If the answer is well formated answer.                        */
String sanswer(void)
{
 uint8_t i = 0;
 String mp3answer = "";
 // Get only 10 Bytes
 while (mp3.available() && (i < 10))
 {
   uint8_t b = mp3.read();
   ansbuf[i] = b;
   i++;
   mp3answer += sbyte2hex(b);
 }
 // if the answer format is correct.
 if ((ansbuf[0] == 0x7E) && (ansbuf[9] == 0xEF))
 {
   return mp3answer;
 }
 return "???: " + mp3answer;
} 

Step 6: POWER

I bought a power module, not sure what the real name “MB-102 MB102” (?)

I connected both Arduinos to this module so the DM console needs only one power input.

Works perfect and minimized the connections outlets to the console.

Step 7: Cosmetic =^)

Step 7.1: Front Panel. Got a lot of help from a good friend (Benny Klingman) using his Fusion 360 skills and as a consultant in other parts of this project. He created the “dxf” file for the laser cutter that we happen to have at the FAB-LAB where we work (I only added my logo at the corner), and “STL” files for all the buttons (we used thenounproject.com for the icons). And this is how it looks before adding all the electronics:

And Here you can see the plan for the layout of the buttons. I used solder-in breadboards to mount the buttons on and solder the jumper wires directly from the other side. It was a bad decision needed to resolder the wires many times during the development of this device – a better practice is to solder wire connectors. I glued (with fast glue) the 3d printed parts of the physical buttons to the electronic buttons. I laser cut an additional wood panel and sandwiched the solder-in breadboards between the two wood panels so the press of the buttons had a counterforce when they’re pushed. There was a need for some buffer wood parts in the sides to prevent unwanted button clicks.

Part 7.2: Caver box.

I had a box in the shape of a book, so I painted the cover with black spray color and then used the laser to engrave some graphics. I needed to cut and replace part of the back panel to arrange a proper place for all the external connections – Power, audio.

Had some challenges figuring how to stable the modules with external connections.

Eventually, I used the laser cutter again to cut a Plexiglas piece that locked the modules from the inside and 3mm Poplar wood from the outside.

Part 8: Finale assembly and functionality

Finale LCD code:

// this code is a DM consul - 1 button shows on LCD 5 random D20 result , 2nd button shows a random speel (from SD card), 3rd button shows a random monster (from SD card).
// 4 - random DM decision, 5 -random magic item, 6 -random NPC (race + trait), 7 - random gold treasure (1-500)
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <SD.h>
File myFile;
int LCD_colums = 20;
int LCD_rows = 4;
LiquidCrystal_I2C lcd(0x3F,LCD_colums,LCD_rows);  // set the LCD address to 0x27 for a 16 chars and 2 line display
int n=0;
int rnd=0;
int DLY=0;
// defining string arrays of races
char *race[22] = {"Dwarf", "Elf", "Halfling" , "Human" , "Gnome" ,"Half-orc" , "Dragonborn", "Tiefling", "Aasimar", "Warforged", 
             "Yuan-ti-Pureblood", "Triton", "Goliath", "Tabaxi", "Half-Elf" , "LizardFolk", "Genasi" , "Aarakocra" ,"Bugbear" , "Kenku", "Githyanki", "Tortle"};
// constants won't change. They're used here to set pin numbers:
const int BUTTON_PIN_D20 = 7; // the number of the d20 pushbutton pin
const int BUTTON_PIN_SPELL = 6; // the number of the spell pushbutton pin
const int BUTTON_PIN_MONSTER = 8; // the number of the monster pushbutton pin
const int BUTTON_PIN_DM = 9; // the number of the DM decision pushbutton pin
const int BUTTON_PIN_item = 5; // the number of the Magic items pushbutton pin
const int BUTTON_PIN_NPC = 4; // the number of the NPC characters pushbutton pin
const int BUTTON_PIN_gold = 3; // the number of the NPC characters pushbutton pin
// Variables will change:
int currentStateD20;    // the current reading from the input pin
int currentStateSPELL;    // the current reading from the input pin
int currentStateMONSTER;
int currentStateDM;    // the current reading from the input pin
int currentStateitem;    // the current reading from the input pin
int currentStateNPC;    // the current reading from the input pin
int currentStategold;    // the current reading from the input pin
void setup()
{
 // initialize serial communication at 9600 bits per second:
 Serial.begin(9600);
 lcd.init();                      // initialize the lcd 
 // Print a message to the LCD.
 lcd.backlight();
 lcd.setCursor(0,1);
 lcd.print(F("Hello, DM Yoni Zabow"));
   // initialize the pushbutton pin as an pull-up input
 // the pull-up input pin will be HIGH when the switch is open and LOW when the switch is closed.
 pinMode(BUTTON_PIN_D20, INPUT_PULLUP);
 pinMode(BUTTON_PIN_SPELL, INPUT_PULLUP);
 pinMode(BUTTON_PIN_MONSTER, INPUT_PULLUP);
 pinMode(BUTTON_PIN_DM, INPUT_PULLUP);
 pinMode(BUTTON_PIN_item, INPUT_PULLUP);
 pinMode(BUTTON_PIN_NPC, INPUT_PULLUP);
 pinMode(BUTTON_PIN_gold, INPUT_PULLUP);
}
// this function finds the needed line (lineNumber) in a txt file from sd card (indecated by filenmber) and shows it on the lcd screen
void printLineN(unsigned int lineNumber, unsigned int filenumber ){
 char rnd_string[LCD_colums+1] = ""; 
 rnd_string[1] = 0; 
 char cr;
 //  SD card initialization
 while (!Serial) {
 ; // wait for serial port to connect. Needed for native USB port only
 }
 Serial.print(F("Initializing SD card..."));
 if (!SD.begin(10)) {
   Serial.println(F("initialization failed!"));
   while (1);
 }
 Serial.println(F("initialization done."));
 // finding the correct file to open
 switch (filenumber) {
   case 1: {myFile = SD.open("monsters.txt");
           break;}
   case 2: {myFile = SD.open("spells.txt");
           break;}
   case 3: {myFile = SD.open("DMD.txt");
           break;}
   case 4: {myFile = SD.open("item.txt");
           break;}
   case 5: {myFile = SD.open("NPCtrait.txt");
           break;}
 }
 myFile.seek(0);
 Serial.println(F("file opened sucssesfuly."));  
 // seeking the right line in the file
 for(unsigned int i = 0; i < (lineNumber -1);){
   cr = myFile.read();
   if(cr == '\n'){
     i++;
   }
 }
 int j=0; //string index
 cr = ' '; // initialization of cr to get inside while loop
 //Now we are at the right line
 while(cr != '\n'){ 
   cr = myFile.read();
   Serial.println(cr);
   rnd_string[j] = cr;
   j++;
   //if string is bigger than LCD max columns than print to LCD and move one row down
   if (j == LCD_colums) { 
     if (rnd_string[j] == '\n') 
        rnd_string[j-1] = 0;
     else if (rnd_string[j] == '\r') 
        rnd_string[j-1] = 0;
     lcd.setCursor(0,1);
     //print first row
     lcd.print(rnd_string);
     // zero the string and the counter
     rnd_string[0] = 0;
     j=0;
   }
 }
     //closing the string without \n\r which adds unwanted char in the end
     rnd_string[j-2] = 0;
     lcd.setCursor(0,2);
     //printing the string to LCD
     lcd.print(rnd_string);
     // close the file:
     myFile.close();
}
void loop()
{
 if (DLY == 7000){
   DLY = 0;
   lcd.clear();
   lcd.setCursor(0,1);
   lcd.print(F("Hello, DM Yoni Zabow"));
 }
 // read the state of the switch/button:
 currentStateD20 = digitalRead(BUTTON_PIN_D20);
 if(currentStateD20 == LOW) 
 {
 lcd.clear();
 lcd.setCursor(1,0);
 lcd.print(F("5 X D20 roll"));
 lcd.setCursor(0,2);
 lcd.print(random(1, 21));
 lcd.setCursor(3,2);
 lcd.print(random(1, 21));
 lcd.setCursor(6,2);
 lcd.print(random(1, 21));
 lcd.setCursor(9,2);
 lcd.print(random(1, 21));
 lcd.setCursor(12,2);
 lcd.print(random(1, 21));
 DLY = 0;
 }
 // read the state of the switch/button:
 currentStateSPELL = digitalRead(BUTTON_PIN_SPELL);
 if(currentStateSPELL == LOW)
 {
  lcd.clear();
  lcd.setCursor(0,0); 
  lcd.print(F("RND Spell Name&LVL:"));
  rnd = random(1, 460);
  Serial.println(rnd);
  printLineN(rnd,2);
  DLY = 0;
 }
currentStateMONSTER = digitalRead(BUTTON_PIN_MONSTER);
if(currentStateMONSTER == LOW)
 {
  lcd.clear();
  lcd.setCursor(0,0); 
  lcd.print(F("Monster Name|AC|HP:"));
  rnd = random(1, 507);
  Serial.println(rnd);
  printLineN(rnd,1);
  DLY = 0;
 }
  currentStateDM = digitalRead(BUTTON_PIN_DM);
if(currentStateDM == LOW)
 {
  lcd.clear();
  lcd.setCursor(0,0); 
  lcd.print(F("RND DM Decision:"));
  rnd = random(1, 25);
  Serial.println(rnd);
  printLineN(rnd,3);
  DLY = 0;
 }
  currentStateitem = digitalRead(BUTTON_PIN_item);
if(currentStateitem == LOW)
 {
  lcd.clear();
  lcd.setCursor(0,0); 
  lcd.print(F("RND Magic item:"));
  rnd = random(1, 240);
  Serial.println(rnd);
  printLineN(rnd,4);
  DLY = 0;
 }
    currentStateNPC = digitalRead(BUTTON_PIN_NPC);
if(currentStateNPC == LOW)
 {
  lcd.clear();
  lcd.setCursor(0,0); 
  lcd.print(F("RND NPC Trait:"));
  rnd = random(1, 1389);
  Serial.println(rnd);
  printLineN(rnd,5);
  lcd.setCursor(0,3); 
  lcd.print("Race: ");
  lcd.setCursor(6,3);
  rnd = random(0, 21); 
  lcd.print(race[rnd]);
  DLY = 0;
 }
 currentStategold = digitalRead(BUTTON_PIN_gold);
if(currentStategold == LOW)
 {
  lcd.clear();
  lcd.setCursor(0,0); 
  lcd.print(F("RND Gold treature:"));
  rnd = random(1, 501);
  Serial.println(rnd);
  lcd.setCursor(0,2); 
  lcd.print(rnd);
  DLY = 0;
 }
 DLY++;
 Serial.println(DLY);
} 

Looks:

DEMO:

filnal product in action

Code

LCDArduino
powering the LCD by the intended buttons
//BUG - the screen freezes after several seconds
//Compatible with the Arduino IDE 1.0
//Library version:1.1
// this code is a DM consul - 1 button shows on LCD 5 random D20 result , 2nd button shows a random speel (from SD card), 3rd button shows a random monster (from SD card).
// 4 - random DM decision, 5 -random magic item, 6 -random NPC (race + trait), 7 - random gold treasure (1-500)
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <SD.h>
File myFile;
int LCD_colums = 20;
int LCD_rows = 4;
LiquidCrystal_I2C lcd(0x3F,LCD_colums,LCD_rows);  // set the LCD address to 0x3F for a 20 chars and 4 line display
int n=0;
int rnd=0;
int DLY=0;
// defining string arrays of races
char *race[22] = {"Dwarf", "Elf", "Halfling" , "Human" , "Gnome" ,"Half-orc" , "Dragonborn", "Tiefling", "Aasimar", "Warforged", 
              "Yuan-ti-Pureblood", "Triton", "Goliath", "Tabaxi", "Half-Elf" , "LizardFolk", "Genasi" , "Aarakocra" ,"Bugbear" , "Kenku", "Githyanki", "Tortle"};


// spell char per row = 34
//int LineLength = 34;
// constants won't change. They're used here to set pin numbers:
const int BUTTON_PIN_D20 = 7; // the number of the d20 pushbutton pin
const int BUTTON_PIN_SPELL = 6; // the number of the spell pushbutton pin
const int BUTTON_PIN_MONSTER = 8; // the number of the monster pushbutton pin
const int BUTTON_PIN_DM = 9; // the number of the DM decision pushbutton pin
const int BUTTON_PIN_item = 5; // the number of the Magic items pushbutton pin
const int BUTTON_PIN_NPC = 4; // the number of the NPC characters pushbutton pin
const int BUTTON_PIN_gold = 3; // the number of the NPC characters pushbutton pin


// Variables will change:

int currentStateD20;    // the current reading from the input pin
int currentStateSPELL;    // the current reading from the input pin
int currentStateMONSTER;
int currentStateDM;    // the current reading from the input pin
int currentStateitem;    // the current reading from the input pin
int currentStateNPC;    // the current reading from the input pin
int currentStategold;    // the current reading from the input pin

void setup()
{
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  lcd.init();                      // initialize the lcd 
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0,1);
  lcd.print(F("Hello, DM Yoni Zabow"));
    // initialize the pushbutton pin as an pull-up input
  // the pull-up input pin will be HIGH when the switch is open and LOW when the switch is closed.
  pinMode(BUTTON_PIN_D20, INPUT_PULLUP);
  pinMode(BUTTON_PIN_SPELL, INPUT_PULLUP);
  pinMode(BUTTON_PIN_MONSTER, INPUT_PULLUP);
  pinMode(BUTTON_PIN_DM, INPUT_PULLUP);
  pinMode(BUTTON_PIN_item, INPUT_PULLUP);
  pinMode(BUTTON_PIN_NPC, INPUT_PULLUP);
  pinMode(BUTTON_PIN_gold, INPUT_PULLUP);
}

// this function finds the needed line (lineNumber) in a txt file from sd card (indecated by filenmber) and shows it on the lcd screen
void printLineN(unsigned int lineNumber, unsigned int filenumber ){
  char rnd_string[LCD_colums+1] = ""; 
  rnd_string[1] = 0; 
  char cr;
  //  SD card initialization
  while (!Serial) {
  ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.print(F("Initializing SD card..."));
  if (!SD.begin(10)) {
    Serial.println(F("initialization failed!"));
    while (1);
  }
  Serial.println(F("initialization done."));

  // finding the correct file to open
  switch (filenumber) {
    case 1: {myFile = SD.open("monsters.txt");
            break;}
    case 2: {myFile = SD.open("spells.txt");
            break;}
    case 3: {myFile = SD.open("DMD.txt");
            break;}
    case 4: {myFile = SD.open("item.txt");
            break;}
    case 5: {myFile = SD.open("NPCtrait.txt");
            break;}
  }
  myFile.seek(0);
  Serial.println(F("file opened sucssesfuly."));  
  
  // seeking the right line in the file
  for(unsigned int i = 0; i < (lineNumber -1);){
    cr = myFile.read();
    if(cr == '\n'){
      i++;
    }
  }
  int j=0; //string index
  cr = ' '; // initialization of cr to get inside while loop
  
  //Now we are at the right line
  while(cr != '\n'){ 
    cr = myFile.read();
    Serial.println(cr);
    rnd_string[j] = cr;
    j++;
    //if string is bigger than LCD max columns than print to LCD and move one row down
    if (j == LCD_colums) { 
      if (rnd_string[j] == '\n') 
         rnd_string[j-1] = 0;
      else if (rnd_string[j] == '\r') 
         rnd_string[j-1] = 0;
      lcd.setCursor(0,1);
      //print first row
      lcd.print(rnd_string);
      // zero the string and the counter
      rnd_string[0] = 0;
      j=0;
    }
  }
      //closing the string without \n\r which adds unwanted char in the end
      rnd_string[j-2] = 0;
      lcd.setCursor(0,2);
      //printing the string to LCD
      lcd.print(rnd_string);
      // close the file:
      myFile.close();
}

void loop()
{
  if (DLY == 7000){
    DLY = 0;
    lcd.clear();
    lcd.setCursor(0,1);
    lcd.print(F("Hello, DM Yoni Zabow"));
  }
  // read the state of the switch/button:
  currentStateD20 = digitalRead(BUTTON_PIN_D20);
  if(currentStateD20 == LOW) 
  {
  lcd.clear();
  lcd.setCursor(1,0);
  lcd.print(F("5 X D20 roll"));
  lcd.setCursor(0,2);
  lcd.print(random(1, 21));
  lcd.setCursor(3,2);
  lcd.print(random(1, 21));
  lcd.setCursor(6,2);
  lcd.print(random(1, 21));
  lcd.setCursor(9,2);
  lcd.print(random(1, 21));
  lcd.setCursor(12,2);
  lcd.print(random(1, 21));
  DLY = 0;
  }
  // read the state of the switch/button:
  currentStateSPELL = digitalRead(BUTTON_PIN_SPELL);
  if(currentStateSPELL == LOW)
  {
   lcd.clear();
   lcd.setCursor(0,0); 
   lcd.print(F("RND Spell Name&LVL:"));
   rnd = random(1, 460);
   Serial.println(rnd);
   printLineN(rnd,2);
   DLY = 0;
  }
 currentStateMONSTER = digitalRead(BUTTON_PIN_MONSTER);
 if(currentStateMONSTER == LOW)
  {
   lcd.clear();
   lcd.setCursor(0,0); 
   lcd.print(F("Monster Name|AC|HP:"));
   rnd = random(1, 507);
   Serial.println(rnd);
   printLineN(rnd,1);
   DLY = 0;
  }
   currentStateDM = digitalRead(BUTTON_PIN_DM);
 if(currentStateDM == LOW)
  {
   lcd.clear();
   lcd.setCursor(0,0); 
   lcd.print(F("RND DM Decision:"));
   rnd = random(1, 25);
   Serial.println(rnd);
   printLineN(rnd,3);
   DLY = 0;
  }
   currentStateitem = digitalRead(BUTTON_PIN_item);
 if(currentStateitem == LOW)
  {
   lcd.clear();
   lcd.setCursor(0,0); 
   lcd.print(F("RND Magic item:"));
   rnd = random(1, 240);
   Serial.println(rnd);
   printLineN(rnd,4);
   DLY = 0;
  }
     currentStateNPC = digitalRead(BUTTON_PIN_NPC);
 if(currentStateNPC == LOW)
  {
   lcd.clear();
   lcd.setCursor(0,0); 
   lcd.print(F("RND NPC Trait:"));
   rnd = random(1, 1389);
   Serial.println(rnd);
   printLineN(rnd,5);
   lcd.setCursor(0,3); 
   lcd.print("Race: ");
   lcd.setCursor(6,3);
   rnd = random(0, 21); 
   lcd.print(race[rnd]);
   DLY = 0;
  }
  currentStategold = digitalRead(BUTTON_PIN_gold);
 if(currentStategold == LOW)
  {
   lcd.clear();
   lcd.setCursor(0,0); 
   lcd.print(F("RND Gold treature:"));
   rnd = random(1, 501);
   Serial.println(rnd);
   lcd.setCursor(0,2); 
   lcd.print(rnd);
   DLY = 0;
  }
  DLY++;
  Serial.println(DLY);
}
MP3Arduino
// COPYIED FROM: https://create.arduino.cc/projecthub/moreirarobotics/how-to-use-the-yx5300-mp3-module-catalex-with-arduino-171a23
// Edited by Dror Moshe Aharoni

// pins connection: 7-12 - music buttons, 5 to mp3 TX, 6 to MP3 RX
#include <SoftwareSerial.h>

#define ARDUINO_RX 5  //should connect to TX of the Serial MP3 Player module
#define ARDUINO_TX 6  //connect to RX of the module

SoftwareSerial mp3(ARDUINO_RX, ARDUINO_TX);
//#define mp3 Serial3    // Connect the MP3 Serial Player to the Arduino MEGA Serial3 (14 TX3 -> RX, 15 RX3 -> TX)

static int8_t Send_buf[8] = {0}; // Buffer for Send commands.  // BETTER LOCALLY
static uint8_t ansbuf[10] = {0}; // Buffer for the answers.    // BETTER LOCALLY

String mp3Answer;           // Answer from the MP3.

String sanswer(void);
String sbyte2hex(uint8_t b);

int BUTTON_PIN;
int last_pushed;
int buttonState;
int currentSong = 1;
int pause = 0;

/************ Command byte **************************/
#define CMD_NEXT_SONG     0X01  // Play next song.
#define CMD_PREV_SONG     0X02  // Play previous song.
#define CMD_PLAY_W_INDEX  0X03
#define CMD_VOLUME_UP     0X04
#define CMD_VOLUME_DOWN   0X05
#define CMD_SET_VOLUME    0X06

#define CMD_SNG_CYCL_PLAY 0X08  // Single Cycle Play.
#define CMD_SEL_DEV       0X09
#define CMD_SLEEP_MODE    0X0A
#define CMD_WAKE_UP       0X0B
#define CMD_RESET         0X0C
#define CMD_PLAY          0X0D
#define CMD_PAUSE         0X0E
#define CMD_PLAY_FOLDER_FILE 0X0F

#define CMD_STOP_PLAY     0X16
#define CMD_FOLDER_CYCLE  0X17
#define CMD_SHUFFLE_PLAY  0x18 //
#define CMD_SET_SNGL_CYCL 0X19 // Set single cycle.

#define CMD_SET_DAC 0X1A
#define DAC_ON  0X00
#define DAC_OFF 0X01

#define CMD_PLAY_W_VOL    0X22
#define CMD_PLAYING_N     0x4C
#define CMD_QUERY_STATUS      0x42
#define CMD_QUERY_VOLUME      0x43
#define CMD_QUERY_FLDR_TRACKS 0x4e
#define CMD_QUERY_TOT_TRACKS  0x48
#define CMD_QUERY_FLDR_COUNT  0x4f

/************ Opitons **************************/
#define DEV_TF            0X02

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


void setup()
{
  // starting the serial communication through the mp3 object
  // The mp3 module is controlled through commands received by the Arduino serial. In this process, we used the SoftwareSerial library and emulated a serial on the Arduino digital pins.
  // Thus, you will be able to use the Arduino to control the MP3 module through commands sent to it.
  Serial.begin(9600);
  mp3.begin(9600);
  delay(500);
  // initialization of the MP3 Card module
  sendCommand(CMD_SEL_DEV, 0, DEV_TF);
  delay(500);
  
  // initialize the pushbutton pins as an pull-up input
  // the pull-up input pin will be HIGH when the switch is open and LOW when the switch is closed.
  for (BUTTON_PIN = 7; BUTTON_PIN < 13; BUTTON_PIN++){
    //BUTTON_PIN is the number of the pushbutton pin
    pinMode(BUTTON_PIN, INPUT_PULLUP); 
  }
  sendCommand(0x06, 0 , 30);
  Serial.println("playing the greetings sound");
  sendCommand(0x03, 0 , 1);
  //delay(19000);
  delay(1000);
  last_pushed = 1;
}

//starting loop
void loop() {
  //Serial.println("start loop");
  buttonState = HIGH;
  BUTTON_PIN = 6;
  // read the state of the switch/button:
  while (buttonState == HIGH){
    BUTTON_PIN++;
    if (BUTTON_PIN == 13) BUTTON_PIN = 7;
    buttonState = digitalRead(BUTTON_PIN);
    // print out the button's state
    //Serial.println(BUTTON_PIN);
    //Serial.println(buttonState);
 }

  //Serial.println("DM pushed button:");
  //Serial.println(BUTTON_PIN);
  if (BUTTON_PIN == last_pushed){
    switch (pause) {
      case 0:
        sendCommand(0x0E);
        last_pushed = BUTTON_PIN;
        pause = 1;
        sanswer();
        break;
      case 1:
        pause = 0;
        sendCommand(0x03, 0, BUTTON_PIN-5);
        if (mp3.available()) Serial.println(decodeMP3Answer());
        break;
    }
    
  }
    
  else 
  {
    // saving the last pushed button in order to enable music pause
    last_pushed = BUTTON_PIN;
        
    // playing the desired music file
    sendCommand(0x03, 0, BUTTON_PIN-5);
    //delay(3000);

    // Check for the answer.
  if (mp3.available()) Serial.println(decodeMP3Answer());
  }

}



/********************************************************************************/
/*Function sendMP3Command: seek for a 'c' command and send it to MP3  */
/*Parameter: c. Code for the MP3 Command, 'h' for help.                                                                                                         */
/*Return:  void                                                                */

void sendMP3Command(char c) {
  
  switch (c) {
    case '?':
    case 'h':
      Serial.println("HELP  ");
      Serial.println(" p = Play");
      Serial.println(" P = Pause");
      Serial.println(" > = Next");
      Serial.println(" < = Previous");
      Serial.println(" s = Stop Play"); 
      Serial.println(" + = Volume UP");
      Serial.println(" - = Volume DOWN");
      Serial.println(" c = Query current file");
      Serial.println(" q = Query status");
      Serial.println(" v = Query volume");
      Serial.println(" x = Query folder count");
      Serial.println(" t = Query total file count");
      Serial.println(" f = Play folder 1.");
      Serial.println(" S = Sleep");
      Serial.println(" W = Wake up");
      Serial.println(" r = Reset");
      break;

    case 'p':
      Serial.println("Play ");
      sendCommand(CMD_PLAY);
      break;

    case 'P':
      Serial.println("Pause");
      sendCommand(CMD_PAUSE);
      break;

    case '>':
      Serial.println("Next");
      sendCommand(CMD_NEXT_SONG);
      sendCommand(CMD_PLAYING_N); // ask for the number of file is playing
      break;

    case '<':
      Serial.println("Previous");
      sendCommand(CMD_PREV_SONG);
      sendCommand(CMD_PLAYING_N); // ask for the number of file is playing
      break;

    case 's':
      Serial.println("Stop Play");
      sendCommand(CMD_STOP_PLAY);
      break;

    case '+':
      Serial.println("Volume Up");
      sendCommand(CMD_VOLUME_UP);
      break;

    case '-':
      Serial.println("Volume Down");
      sendCommand(CMD_VOLUME_DOWN);
      break;

    case 'c':
      Serial.println("Query current file");
      sendCommand(CMD_PLAYING_N);
      break;

    case 'q':
      Serial.println("Query status");
      sendCommand(CMD_QUERY_STATUS);
      break;

    case 'v':
      Serial.println("Query volume");
      sendCommand(CMD_QUERY_VOLUME);
      break;

    case 'x':
      Serial.println("Query folder count");
      sendCommand(CMD_QUERY_FLDR_COUNT);
      break;

    case 't':
      Serial.println("Query total file count");
      sendCommand(CMD_QUERY_TOT_TRACKS);
      break;

    case 'f':
      Serial.println("Playing folder 1");
      sendCommand(CMD_FOLDER_CYCLE, 1, 0);
      break;

    case 'S':
      Serial.println("Sleep");
      sendCommand(CMD_SLEEP_MODE);
      break;

    case 'W':
      Serial.println("Wake up");
      sendCommand(CMD_WAKE_UP);
      break;

    case 'r':
      Serial.println("Reset");
      sendCommand(CMD_RESET);
      break;
  }
}

/********************************************************************************/
/*Function decodeMP3Answer: Decode MP3 answer.                                  */
/*Parameter:-void                                                               */
/*Return: The                                                  */

String decodeMP3Answer() {
  String decodedMP3Answer = "";

  decodedMP3Answer += sanswer();

  switch (ansbuf[3]) {
    case 0x3A:
      decodedMP3Answer += " -> Memory card inserted.";
      break;

    case 0x3D:
      decodedMP3Answer += " -> Completed play num " + String(ansbuf[6], DEC);
      //sendCommand(CMD_NEXT_SONG);
      //sendCommand(CMD_PLAYING_N); // ask for the number of file is playing
      break;

    case 0x40:
      decodedMP3Answer += " -> Error";
      break;

    case 0x41:
      decodedMP3Answer += " -> Data recived correctly. ";
      break;

    case 0x42:
      decodedMP3Answer += " -> Status playing: " + String(ansbuf[6], DEC);
      break;

    case 0x48:
      decodedMP3Answer += " -> File count: " + String(ansbuf[6], DEC);
      break;

    case 0x4C:
      decodedMP3Answer += " -> Playing: " + String(ansbuf[6], DEC);
      break;

    case 0x4E:
      decodedMP3Answer += " -> Folder file count: " + String(ansbuf[6], DEC);
      break;

    case 0x4F:
      decodedMP3Answer += " -> Folder count: " + String(ansbuf[6], DEC);
      break;
  }

  return decodedMP3Answer;
}

/********************************************************************************/
/*Function: Send command to the MP3                                             */
/*Parameter: byte command                                                       */
/*Parameter: byte dat1 parameter for the command                                */
/*Parameter: byte dat2 parameter for the command                                */

void sendCommand(byte command){
  sendCommand(command, 0, 0);
}

void sendCommand(byte command, byte dat1, byte dat2){
  delay(20);
  Send_buf[0] = 0x7E;    //
  Send_buf[1] = 0xFF;    //
  Send_buf[2] = 0x06;    // Len
  Send_buf[3] = command; //
  Send_buf[4] = 0x01;    // 0x00 NO, 0x01 feedback
  Send_buf[5] = dat1;    // datah
  Send_buf[6] = dat2;    // datal
  Send_buf[7] = 0xEF;    //
  Serial.print("Sending: ");
  for (uint8_t i = 0; i < 8; i++)
  {
    mp3.write(Send_buf[i]) ;
    Serial.print(sbyte2hex(Send_buf[i]));
  }
  Serial.println();
}



/********************************************************************************/
/*Function: sbyte2hex. Returns a byte data in HEX format.                       */
/*Parameter:- uint8_t b. Byte to convert to HEX.                                */
/*Return: String                                                                */


String sbyte2hex(uint8_t b)
{
  String shex;

  shex = "0X";

  if (b < 16) shex += "0";
  shex += String(b, HEX);
  shex += " ";
  return shex;
}


/********************************************************************************/
/*Function: shex2int. Returns a int from an HEX string.                         */
/*Parameter: s. char *s to convert to HEX.                                      */
/*Parameter: n. char *s' length.                                                */
/*Return: int                                                                   */

int shex2int(char *s, int n){
  int r = 0;
  for (int i=0; i<n; i++){
     if(s[i]>='0' && s[i]<='9'){
      r *= 16; 
      r +=s[i]-'0';
     }else if(s[i]>='A' && s[i]<='F'){
      r *= 16;
      r += (s[i] - 'A') + 10;
     }
  }
  return r;
}


/********************************************************************************/
/*Function: sanswer. Returns a String answer from mp3 UART module.          */
/*Parameter:- uint8_t b. void.                                                  */
/*Return: String. If the answer is well formated answer.                        */

String sanswer(void)
{
  uint8_t i = 0;
  String mp3answer = "";

  // Get only 10 Bytes
  while (mp3.available() && (i < 10))
  {
    uint8_t b = mp3.read();
    ansbuf[i] = b;
    i++;

    mp3answer += sbyte2hex(b);
  }

  // if the answer format is correct.
  if ((ansbuf[0] == 0x7E) && (ansbuf[9] == 0xEF))
  {
    return mp3answer;
  }

  return "???: " + mp3answer;
}

Custom parts and enclosures

front panel
Front panel
frontpanel_R8NQVWruIJ.dxf
Buttons
STL files for all the buttons I 3D printed
(the button with the dragon for the RND monster has 2 vertions)
dm_consule_stl_buttons_XqoINwiEKa.zip

Schematics

complete circuit
This is the complete circuit sketch.
(I used a BlueTooth module to represent the XY6300 UART TTL MP3 module)
The power module is connected to the Vin and GND of both Arduinos.
The buttons arranged in one row for simplicity,
all_circuit_sketch_1cWKSKok5G.fzz

Comments

Author

Ffd267da d08a 4b54 abec 22cede165a3c
droraha
  • 1 project
  • 0 followers

Additional contributors

Published on

June 25, 2021

Members who respect this project

Aula
See similar projects
you might like

Similar projects you might like

Tic Tac Toe and Lights Out Game

Project tutorial by markbennettuk

  • 2,195 views
  • 0 comments
  • 5 respects

Arduino Dot Matrix Game Console

Project tutorial by Md. Khairul Alam

  • 15,799 views
  • 2 comments
  • 40 respects

Ingegno Retro Games Console

Project tutorial by bmcage

  • 5,292 views
  • 6 comments
  • 17 respects

Voltmeter Clock

Project tutorial by markbennettuk

  • 2,319 views
  • 0 comments
  • 8 respects

Smart Disinfection and Sanitation Tunnel

Project tutorial by Yash Nayak

  • 58,164 views
  • 72 comments
  • 112 respects

Earth_Analyzer

Project tutorial by gowthamkishoreindukuri

  • 10,223 views
  • 10 comments
  • 45 respects
Add projectSign up / Login