資料介紹
描述
對(duì)于我的第一個(gè) Arduino 項(xiàng)目,我決定制作一個(gè) DM 控制臺(tái)作為生日禮物送給一位兒時(shí)的摯友。
對(duì)于那些希望看到最終產(chǎn)品的人,請(qǐng)點(diǎn)擊此鏈接。
它從功能和外觀的草圖開(kāi)始:

中間有一個(gè) LCD 屏幕,周?chē)写蠹s 13 個(gè)按鈕。1 個(gè)用于 D20 滾動(dòng)的按鈕,1 個(gè)用于隨機(jī)金寶的按鈕,5 個(gè)用于從文件中生成隨機(jī)文本的按鈕(魔法物品、怪物、NPC、法術(shù)、DM 決定),6 個(gè)用于背景音樂(lè)的按鈕。
我從一個(gè)從未開(kāi)始的項(xiàng)目中獲得了我需要的大部分硬件。所以首先我下載了?? Arduino Desktop IDE。我的電腦無(wú)法識(shí)別 Arduino——中國(guó)芯片所需的驅(qū)動(dòng)程序。現(xiàn)在我可以開(kāi)始了。
很快我就明白我需要一張用于文件的 SD 卡和一張用于音樂(lè)的 SD 卡。完畢。
第 1 步:連接 LCD 屏幕并進(jìn)行打印。
使用此鏈接很容易。獲得更大的屏幕并使用此處步驟 1 中的代碼來(lái)計(jì)算 LCD 的正確地址。我寫(xiě)了以下代碼:
//YWROBOT
//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include
#include
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++;
}
第二步:生成隨機(jī)數(shù)。
使用Random 函數(shù),我編寫(xiě)了一個(gè)測(cè)試代碼——在屏幕上生成 1 到 20 之間的隨機(jī)數(shù),上面有文字:
#include
#include
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));
}
第 3 步:連接并編碼一個(gè)按鈕以使其工作。
我學(xué)會(huì)了如何使用這個(gè)鏈接做到這一點(diǎn)。
D20 的代碼就是這樣誕生的:

#include
#include
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);
}
}
第 4 步:從 SD 卡中讀取特定行。

首先,我學(xué)會(huì)了讀寫(xiě) SD 卡。但這就是讀取整個(gè)文件的方法,我只需要一個(gè)特定的行!幸運(yùn)的是,我找到了這篇文章并使用了@septillion代碼:
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
}
我必須承認(rèn),我一開(kāi)始并不理解代碼并且害怕使用它。尋找其他解決方案(甚至考慮構(gòu)建一個(gè)服務(wù)器來(lái)與 Arduino 與 WIFI 通信),最終有勇氣使用這段代碼并將其用于我的項(xiàng)目 - 它就像魔術(shù)一樣工作。
另一位朋友 (Ofir Sofer) 幫助我處理了 TXT 文件的內(nèi)容,而我正在攻克這一領(lǐng)域。我構(gòu)建了以下函數(shù),它工作正常,但有一個(gè)小錯(cuò)誤——一個(gè)不需要的整理字符。

經(jīng)過(guò)多次嘗試,我通過(guò)添加以下代碼行找到了此錯(cuò)誤的解決方案:
//closing the string without \n\r which adds unwanted char in the end
rnd_string[j-2]= 0;
以及整個(gè)功能代碼:
// 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();
}
第 5 步:無(wú)法連接和使用 MP3 播放器模塊
我有這部分“HW-195-JL”:
這是一個(gè)有用的部分——有一個(gè)非常強(qiáng)大的放大器,作為一個(gè)獨(dú)立的工作——所以我需要弄清楚如何向它發(fā)送命令并繼續(xù)使用 Arduino 代碼。但它只有 3 個(gè)控件!返回 | 播放 | 固件。我希望它在觸發(fā)按鈕時(shí)播放特定文件。我決定嘗試使用固件跳過(guò)需要的文件并找到我希望播放的文件。編碼感覺(jué)很容易。與 Arduino 的連接是一個(gè)巨大的挑戰(zhàn)!
Step5.1:焊接和理解mp3播放器

音頻輸出是單聲道的,我將它連接到 3.5 毫米母插孔并將 RNG 連接到 TIP 以獲得半立體聲輸出:

Step5.2:通過(guò)Arduino推薦控制mp3模塊。
這幾乎讓我崩潰了。觸發(fā)器僅適用于 3v 信號(hào)。無(wú)論我嘗試什么,我都找不到從 Arduino 發(fā)送這樣一個(gè)信號(hào)的方法。
然后它擊中了我!我需要使用晶體管來(lái)控制 mp3 模塊。
讓我自己 3 個(gè) 2n2222晶體管連接到 E 到 GND,B 到 Arduino 引腳,C 到另一個(gè)連接,并嘗試了一下。沒(méi)用?。經(jīng)過(guò)一番谷歌搜索,我知道我需要一個(gè) 1KΩ 的電阻器(為了替換 2n2222,我可能會(huì)用 5V 燒毀)。最后,它奏效了!
[然后我決定把所有東西都焊接得更好,但它不再起作用了——我放棄了這部分,買(mǎi)了另一個(gè)模塊]
Step5.3:編寫(xiě)代碼。
畢竟,到目前為止,我了解到,這部分的代碼感覺(jué)非常簡(jiǎn)單。
因此,我編寫(xiě)了代碼,并在循環(huán)代碼開(kāi)始運(yùn)行時(shí)聽(tīng)到了可怕的跳過(guò)聲音。經(jīng)過(guò)一小時(shí)的逐個(gè)檢查代碼和硬件連接后,我認(rèn)為 Arduino 中的 PIN 3 是導(dǎo)致它的原因,并將一個(gè)按鈕移到另一個(gè)引腳。有效!直到它不起作用?
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;
}
}
再次第 5 步 – 從頭開(kāi)始?? MP3 播放器
我又買(mǎi)了一個(gè)模塊——YX6300 UART TTL串口MP3音樂(lè)播放模塊

這個(gè)模塊要簡(jiǎn)單得多,但缺少放大器。
發(fā)現(xiàn)這篇文章讓一切變得簡(jiǎn)單。我很快就編寫(xiě)了代碼,唯一的挑戰(zhàn)是弄清楚模塊如何對(duì)文件進(jìn)行排序——按創(chuàng)建日期。
該模塊能夠通過(guò) Arduino 和模塊之間的通信來(lái)播放特定文件以及更多功能(通信功能在我鏈接的帖子中的代碼中)。
壓軸 MP3 代碼:
// 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
#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 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;
}
第 6 步:電源
我買(mǎi)了一個(gè)電源模塊,不知道真名是什么“MB-102 MB102”(?)

我將兩個(gè) Arduino 都連接到該模塊,因此 DM 控制臺(tái)只需要一個(gè)電源輸入。
完美運(yùn)行,并最大限度地減少了到控制臺(tái)的連接插座。
第7步:化妝品=^)
步驟 7.1:前面板。一位好朋友 (Benny Klingman) 使用他的Fusion 360技能獲得了很多幫助,并在該項(xiàng)目的其他部分擔(dān)任顧問(wèn)。他為我們工作的 FAB-LAB 中碰巧擁有的激光切割機(jī)創(chuàng)建了“dxf”文件(我只在角落添加了我的徽標(biāo)),并為所有按鈕創(chuàng)建了“STL”文件(我們使用了 thenounproject.com圖標(biāo))。這是添加所有電子設(shè)備之前的樣子:

在這里您可以看到按鈕布局的計(jì)劃。我使用焊接面包板來(lái)安裝按鈕并直接從另一側(cè)焊接跳線(xiàn)。在該設(shè)備的開(kāi)發(fā)過(guò)程中多次重新焊接電線(xiàn)是一個(gè)錯(cuò)誤的決定——更好的做法是焊接電線(xiàn)連接器。我將物理按鈕的 3D 打印部分粘合(用快速膠水)到電子按鈕上。我用激光切割了一塊額外的木板,并將焊接的面包板夾在兩塊木板之間,這樣在按下按鈕時(shí)就會(huì)產(chǎn)生反作用力。側(cè)面需要一些緩沖木部件,以防止不必要的按鈕點(diǎn)擊。

?

第 7.2 部分:洞穴箱。
我有一個(gè)書(shū)本形狀的盒子,所以我用黑色噴漆涂了封面,然后用激光雕刻了一些圖形。我需要切割和更換后面板的一部分,以便為所有外部連接(電源、音頻)安排一個(gè)合適的位置。
在確定如何通過(guò)外部連接穩(wěn)定模塊時(shí)遇到了一些挑戰(zhàn)。
最后,我再次使用激光切割機(jī)切割了一塊有機(jī)玻璃,從內(nèi)部鎖定模塊,從外部鎖定 3 毫米白楊木。

第 8 部分:壓軸組裝和功能
壓軸液晶代碼:
// 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
#include
#include
#include
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);
}
看起來(lái):

演示:
?
- 如何制作ATtiny85控制臺(tái)-ArduPlay
- Arduino Arduboy控制臺(tái)開(kāi)源分享
- RT-Thread文檔_FinSH 控制臺(tái)
- 帶霍爾傳感器的賽艇控制臺(tái)
- Arduino藍(lán)牙控制車(chē)開(kāi)源分享
- Arduino控制時(shí)鐘和動(dòng)畫(huà)開(kāi)源分享
- Arduino控制的ecu speeduino開(kāi)源
- Raspberry Pi串行控制臺(tái)迷你帽
- Cartrdige Vectrex控制臺(tái)開(kāi)源項(xiàng)目
- 帶有LFO的Atari Punk控制臺(tái)
- SE的Atari Punk控制臺(tái)
- SU RUBy控制臺(tái)如何使用?SU RUBy控制臺(tái)的一個(gè)應(yīng)用實(shí)例說(shuō)明 0次下載
- 基于LabVIEW的高速相機(jī)同步控制臺(tái)_任小均 19次下載
- windows控制臺(tái)常用命令解釋 0次下載
- 激光測(cè)距機(jī)顯示控制臺(tái)的設(shè)計(jì)
- 控制臺(tái)窗口主機(jī)是什么 2419次閱讀
- 一個(gè)簡(jiǎn)易的控制臺(tái)程顯示程序 464次閱讀
- 如何使用Python和PinPong庫(kù)控制Arduino 1033次閱讀
- 如何使用Rust的標(biāo)準(zhǔn)庫(kù)和structopt庫(kù)來(lái)處理控制臺(tái)參數(shù) 1378次閱讀
- 基于全志A133處理器DIY的平板電腦 6519次閱讀
- 如何從網(wǎng)頁(yè)控制arduino? 4186次閱讀
- 基于單片機(jī)IAP技術(shù)和aLABVIEW實(shí)現(xiàn)遠(yuǎn)程多對(duì)象控制系統(tǒng)的設(shè)計(jì) 2817次閱讀
- 采用單片機(jī)和MCP2510、MCP2551芯片設(shè)計(jì)集散型火災(zāi)報(bào)警控制系統(tǒng) 4927次閱讀
- dfrobot Arduino Nano控制板介紹 5954次閱讀
- dfrobot模擬PH計(jì) (Arduino兼容)簡(jiǎn)介 2118次閱讀
- dfrobotRomeo 三合一Arduino兼容控制器介紹 2580次閱讀
- arduino如何控制舵機(jī)及詳細(xì)步驟 16.5w次閱讀
- 淺談Arduino和樹(shù)莓派的區(qū)別 2.1w次閱讀
- arduino開(kāi)發(fā)板有什么用 2.1w次閱讀
- dmx512控制臺(tái)是什么_dmx512控制臺(tái)按鍵說(shuō)明 2.7w次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數(shù)據(jù)手冊(cè)
- 1.06 MB | 532次下載 | 免費(fèi)
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費(fèi)
- 3TC358743XBG評(píng)估板參考手冊(cè)
- 1.36 MB | 330次下載 | 免費(fèi)
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費(fèi)
- 5元宇宙深度解析—未來(lái)的未來(lái)-風(fēng)口還是泡沫
- 6.40 MB | 227次下載 | 免費(fèi)
- 6迪文DGUS開(kāi)發(fā)指南
- 31.67 MB | 194次下載 | 免費(fèi)
- 7元宇宙底層硬件系列報(bào)告
- 13.42 MB | 182次下載 | 免費(fèi)
- 8FP5207XR-G1中文應(yīng)用手冊(cè)
- 1.09 MB | 178次下載 | 免費(fèi)
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 2555集成電路應(yīng)用800例(新編版)
- 0.00 MB | 33566次下載 | 免費(fèi)
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費(fèi)
- 4開(kāi)關(guān)電源設(shè)計(jì)實(shí)例指南
- 未知 | 21549次下載 | 免費(fèi)
- 5電氣工程師手冊(cè)免費(fèi)下載(新編第二版pdf電子書(shū))
- 0.00 MB | 15349次下載 | 免費(fèi)
- 6數(shù)字電路基礎(chǔ)pdf(下載)
- 未知 | 13750次下載 | 免費(fèi)
- 7電子制作實(shí)例集錦 下載
- 未知 | 8113次下載 | 免費(fèi)
- 8《LED驅(qū)動(dòng)電路設(shè)計(jì)》 溫德?tīng)栔?/a>
- 0.00 MB | 6656次下載 | 免費(fèi)
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費(fèi)
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537798次下載 | 免費(fèi)
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費(fèi)
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費(fèi)
- 6電路仿真軟件multisim 10.0免費(fèi)下載
- 340992 | 191187次下載 | 免費(fèi)
- 7十天學(xué)會(huì)AVR單片機(jī)與C語(yǔ)言視頻教程 下載
- 158M | 183279次下載 | 免費(fèi)
- 8proe5.0野火版下載(中文版免費(fèi)下載)
- 未知 | 138040次下載 | 免費(fèi)
評(píng)論