遠端WEB控制MP3播放器設計(基於mini2440)
阿新 • • 發佈:2019-01-01
網上有很多 基於mini2440的MP3播放器設計的資料,多是按鍵控制,這裡博主做了些輕微改動,利用遠端WEB來控制MP3播放,具體怎麼實現,下面會給出,大家先看看效果:
WEB介面:
後臺執行:
因為不是什麼課程設計報告,博主就不闡述的那麼詳細,這個設計主要有三部分組成:
1、WEB控制端(就是瀏覽器);
2、WEB伺服器端(將mini2440開發板作為伺服器,其中移植了BOA伺服器,這裡不做詳細介紹,大家網上搜一搜,好多資料,BOA伺服器是嵌入式裝置中用的比較多的WEB伺服器);
3、應用程式部分(就是MP3播放器);
下面將程式碼列出,就其中重要部分進行詳解:
MP3播放程式碼如下:
MP3播放的實現答題架構沒有變,主要修改部分是控制部分,原來控制是由按鍵中斷來實現的,現將其改為由程序間通訊——命名管道來控制,現將主要部分程式碼列出:/* * SD卡mp3播放器控制程式 * 功能: k1:播放、暫停 k2:停止播放 k3:上一首 k4:下一首 * 附加:歌曲自動迴圈播放SD卡/sdcard/song目錄中mp3歌曲 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h> #include <sys/wait.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> /*共享記憶體申請標記*/ #define PERM S_IRUSR|S_IWUSR /*雙向迴圈列表:存放歌曲名*/ struct song { char songname[20]; struct song *prev; struct song *next; }; /*孫子程序id號*/ pid_t gradchild; /*子程序id號*/ pid_t pid; /*共享記憶體描述標記*/ int shmid; char *p_addr; /*共享記憶體內容格式*/ /*|gradchild(孫子程序PID) |+ |空一個位元組|+ currentsong(當前播放列表的節點指標)|*/ /*播放標記*/ int first_key=1; int play_flag=0; /************************************************* Function name: play Parameter : struct song * Description : 播放函式 Return : void Argument : void **************************************************/ void play(struct song *currentsong) { pid_t fd; char *c_addr; char *p; int len; char my_song[30]="/sdcard/song/"; while(currentsong) { /*建立子程序,即孫子程序*/ fd = fork(); if(fd == -1) { perror("fork"); exit(1); } else if(fd == 0) { /*把歌曲名加上根路徑*/ strcat(my_song,currentsong->songname); p = my_song; len = strlen(p); /*去掉檔名最後的'\n'*/ my_song[len-1]='\0'; printf("THIS SONG IS %s\n",my_song); /*執行madplay播放器,播放MP3*/ system("printf(" THIS SONG IS %s\n", $my_song) >>/tmp/songname"); execl("/bin/madplay","madplay",my_song,NULL); printf("\n\n\n"); } else { /*記憶體對映*/ c_addr = shmat(shmid,0,0); memcpy(c_addr,&fd,sizeof(pid_t)); memcpy(c_addr + sizeof(pid_t)+1,¤tsong,4); /*使用wait阻塞子程序,直到孫子程序播放完才能被喚醒; 當被喚醒時,表示播放MP3期間沒有按鍵按下,則繼續順序播放下一首MP3*/ if(fd == wait(NULL)) { currentsong = currentsong->next; printf("THE NEXT SONG IS %s\n",currentsong->songname); } } } } /************************************************* Function name: creat_song_list Parameter : void Description : 建立歌曲名的雙向迴圈連結串列 Return : struct song * Argument : void **************************************************/ struct song *creat_song_list(void) { FILE *fd; size_t size; size_t len; char *line = NULL; struct song *head; struct song *p1; struct song *p2; system("ls /sdcard/song >song_list"); fd = fopen("song_list","r"); p1 = (struct song *)malloc(sizeof(struct song)); printf("==================================song list=====================================\n"); system("ls /sdcard/song"); printf("\n"); printf("================================================================================\n"); size = getline(&line,&len,fd); strncpy(p1->songname,line,strlen(line)); head = p1; while((size = getline(&line,&len,fd)) != -1) //從檔案中讀取一行,直到出錯或者到檔案尾EOF返回-1 { p2 = p1; p1 = (struct song *)malloc(sizeof(struct song)); strncpy(p1->songname,line,strlen(line)); p2->next = p1; p1->prev = p2; } //此時到了檔案尾,若是迴圈列表,則下一首為head,即第一首 p1->next = head; head->prev = p1; p1 = NULL; p2 = NULL; system("rm -rf song_list"); return head; } /************************************************* Function name: startplay Parameter : pid_t *,struct song * Description : 開始播放函式 Return : void Argument : void **************************************************/ void startplay(pid_t *childpid,struct song *my_song) { pid_t pid; int ret; /*建立子程序*/ pid = fork(); if(pid > 0) //父程序 { *childpid = pid; //子程序PID初始化 play_flag = 1; sleep(1); /*讀取共享記憶體儲存的pid,初始化孫子程序的pid*/ memcpy(&gradchild,p_addr,sizeof(pid_t)); } else if(0 == pid) //子程序 { /*子程序播放MP3函式*/ play(my_song); } } /************************************************* Function name: my_pause Parameter : pid_t Description : 暫停函式 Return : void Argument : void **************************************************/ void my_pause(pid_t pid) { printf("=======================PAUSE!PRESS K1 TO CONTINUE===================\n"); kill(pid,SIGSTOP); //對孫子程序傳送SIGSTOP訊號 play_flag = 0; } /************************************************* Function name: my_pause Parameter : pid_t Description : 停止播放函式 Return : void Argument : void Autor & date : Hanson 11,04, 05 **************************************************/ void my_stop(pid_t g_pid) { printf("=======================STOP!PRESS K1 TO START PLAY===================\n"); kill(g_pid,SIGKILL); //對孫子程序傳送SIGKILL訊號 kill(pid,SIGKILL); //對子程序傳送SIGKILL訊號 first_key=1; } /************************************************* Function name: conti_play Parameter : pid_t Description : 繼續函式 Return : void Argument : void **************************************************/ void conti_play(pid_t pid) { printf("===============================CONTINUE=============================\n"); kill(pid,SIGCONT); //對孫子程序傳送SIGCONT訊號 play_flag=1; } /************************************************* Function name: next Parameter : pid_t Description : 下一首函式 Return : void Argument : void **************************************************/ void next(pid_t next_pid) { struct song *nextsong; printf("===============================NEXT MP3=============================\n"); /*從共享記憶體獲得孫子程序播放歌曲的節點指標*/ memcpy(&nextsong,p_addr + sizeof(pid_t)+1,4); /*指向下首歌曲的節點*/ nextsong = nextsong->next; /*殺死當前歌曲播放的子程序,孫子程序*/ kill(pid,SIGKILL); kill(next_pid,SIGKILL); wait(NULL); startplay(&pid,nextsong); } /************************************************* Function name: prev Parameter : pid_t Description : 上一首函式 Return : void Argument : void **************************************************/ void prev(pid_t prev_pid) { struct song *prevsong; /*從共享記憶體獲得孫子程序播放歌曲的節點指標*/ printf("===============================PRIOR MP3=============================\n"); memcpy(&prevsong,p_addr + sizeof(pid_t)+1,4); /*指向上首歌曲的節點*/ prevsong = prevsong->prev; /*殺死當前歌曲播放的子程序,孫子程序*/ kill(pid,SIGKILL); kill(prev_pid,SIGKILL); wait(NULL); startplay(&pid,prevsong); } /************************************************* Function name: main Parameter : void Description : 主函式 Return : int Argument : void **************************************************/ int main(void) { // int buttons_fd; int mp3_control_pipe; int mp3_control_pipe_write; // int key_value; int playnum; // char playnum; struct song *head; /*開啟裝置檔案*/ // buttons_fd = open("/dev/key", 0); // if (buttons_fd < 0) { // perror("open device buttons"); // exit(1); // } /*建立命名管道mp3-control*/ unlink("/tmp/mp3_control"); mkfifo("/tmp/mp3_control",0666); mp3_control_pipe=open("/tmp/mp3_control",O_RDONLY|O_NONBLOCK); mp3_control_pipe_write=open("/tmp/mp3_control",O_WRONLY|O_NONBLOCK); if(mp3_control_pipe<0) { perror("open control pipe for read"); } printf("**********基於mini2440的網頁控制MP3播放器*********** \n"); printf("********************XXX製作 ************************ \n"); printf("**************嵌入式開發專案************************ \n"); /*建立播放列表*/ head = creat_song_list(); printf("===================================FUNTION======================================\n\n"); printf(" K1:播放、暫停 K2:停止播放 K3:下一歌曲 K4:上一歌曲 \n\n"); printf("================================================================================\n"); /*共享記憶體:用於存放子程序ID,播放列表位置*/ if((shmid = shmget(IPC_PRIVATE,5,PERM))== -1) exit(1); p_addr = shmat(shmid,0,0); memset(p_addr,'\0',1024); while(1) { fd_set rds;/*先宣告一個 fd_set 集合來儲存我們要檢測的 buttons_fd控制代碼*/ int ret1; FD_ZERO(&rds);/*用select函式之前將rds清零*/ FD_SET(mp3_control_pipe, &rds);/*將檢測到的控制代碼放到集合rds裡*/ /*監聽獲取鍵值*/ //struct timeval timeout = {30,0}; ret1 = select(mp3_control_pipe + 1, &rds, NULL, NULL,NULL); //printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret1); if (ret1 < 0) { perror("select"); exit(1); } if (ret1 == 0) printf("Timeout.\n"); else if (FD_ISSET(mp3_control_pipe, &rds)) { static char buffer[10]; int ret = read(mp3_control_pipe, &buffer, 2); // printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret); if (ret != 2) { if (errno != EAGAIN) { perror("read buttons\n"); continue; } } else { int playnum1; if(sscanf(buffer,"%d",&playnum1)==1) { playnum=playnum1; } memset(buffer,0,sizeof(buffer)); printf("buttons_value: %d\n", playnum); /*首次播放,必須是按鍵1*/ if(first_key){ switch(playnum) { case 0: startplay(&pid,head); first_key=0; break; case 1: case 2: case 3: printf("=======================PRESS K1 TO START PLAY===================\n"); break; default: printf("=======================PRESS K1 TO START PLAY===================\n"); break; } //end switch }//end if(first_key) /*若不是首次播放,則根據不同鍵值處理*/ else if(!first_key){ switch(playnum) { case 0: //printf("play_flag:%d\n",play_flag); if(play_flag) my_pause(gradchild); else conti_play(gradchild); break; case 1: my_stop(gradchild); break; case 2: next(gradchild); break; case 3: prev(gradchild); break; } //end switch }//end if(!first_key) } } } close(mp3_control_pipe); return 0; }
這部分主要是建立一個命名管道mp3_control,以實現程序間通訊,原來博主只是為了在MP3模組中不斷的讀取管道里面的資料,所以第一次時只是單獨的讀:int main(void) { int mp3_control_pipe; int mp3_control_pipe_write; int playnum; struct song *head; /*開啟裝置檔案*/ *建立命名管道mp3-control*/ unlink("/tmp/mp3_control"); mkfifo("/tmp/mp3_control",0666); mp3_control_pipe=open("/tmp/mp3_control",O_RDONLY|O_NONBLOCK); mp3_control_pipe_write=open("/tmp/mp3_control",O_WRONLY|O_NONBLOCK); if(mp3_control_pipe<0) { perror("open control pipe for read"); }
mp3_control_pipe=open("/tmp/mp3_control",O_RDONLY|O_NONBLOCK);
但是後面執行時,卻一直讀取管道里面的內容,就是對一個相同的資料不斷地讀取,成了一個死迴圈,最後查詢資料把寫管道也打開了,這樣才OK了,大家知道是什麼原因嗎?博主查找了相關資料,卻沒找到答案,只在某地看到這個,大家看看是什麼意思,為什麼要把讀和寫都開啟
下面這段程式碼是不斷的監聽管道里面的數值,並作出相應操作
/*共享記憶體:用於存放子程序ID,播放列表位置*/ if((shmid = shmget(IPC_PRIVATE,5,PERM))== -1) exit(1); p_addr = shmat(shmid,0,0); memset(p_addr,'\0',1024); while(1) { fd_set rds;/*先宣告一個 fd_set 集合來儲存我們要檢測的 buttons_fd控制代碼*/ int ret1; FD_ZERO(&rds);/*用select函式之前將rds清零*/ FD_SET(mp3_control_pipe, &rds);/*將檢測到的控制代碼放到集合rds裡*/ /*監聽獲取鍵值*/ //struct timeval timeout = {30,0}; ret1 = select(mp3_control_pipe + 1, &rds, NULL, NULL,NULL); //printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret1); if (ret1 < 0) { perror("select"); exit(1); } if (ret1 == 0) printf("Timeout.\n"); else if (FD_ISSET(mp3_control_pipe, &rds)) { static char buffer[10]; int ret = read(mp3_control_pipe, &buffer, 2); // printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret); if (ret != 2) { if (errno != EAGAIN) { perror("read buttons\n"); continue; } } else { int playnum1; if(sscanf(buffer,"%d",&playnum1)==1) { playnum=playnum1; } memset(buffer,0,sizeof(buffer)); printf("buttons_value: %d\n", playnum);
二、BOA伺服器
BOA伺服器這裡先不詳細介紹,主要通過呼叫CGI程式實現與應用程式的資訊互動,這裡就是呼叫CGI程式向管道里寫資料的,程式碼如下:
#!/bin/sh
type=0
case $QUERY_STRING in
*play*)
type=0
;;
*stop*)
type=1
;;
*priv*)
type=2
;;
*next*)
type=3
;;
esac
/bin/echo $type > /tmp/mp3_control
echo "Content-type: text/html; charset=gb2312"
echo
/bin/cat mp3-result.template
exit 0
這裡是用shell指令碼編寫的,當然也可以用C來編寫,具體BOA伺服器和CGI博主打算單獨寫在一篇文章裡。三、WEB控制部分
博主對WEB開發沒有學習過,只是利用別人的指令碼進行簡單更改,程式碼如下:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>網頁控制MP3</title>
<style type="text/css">
<!--
a:link { color:#FFFFFF; text-decoration: none }
a:visited { color:#FFFFFF; text-decoration: none}
a:hover { color: #FF6600; text-decoration: underline}
a.develop_item_link:link {color:#FF6600; font-size: 9pt; text-decoration:underline}
a.left_item_style:hover {color:green; font-size: 9pt; text-decoration:none}
a.develop_item_style:hover {color:"#00FF00"; font-size: 9pt; text-decoration:none}
a.download_item_style:hover {color:bule; font-size: 9pt; text-decoration:none}
a.middle_item_style:hover {color:red; font-size: 12pt; text-decoration:underline}
a.middle_item_style:link { color: #000000; text-decoration: none}
a.middle_item_style:visited { color:#000000; text-decoration: none}
.STYLE13 {color:#FFFFFF; text-decoration: none}
.STYLE14 {
font-size: 16px;
font-weight: bold;
}
-->
</style>
<script language="JavaScript">
<!--
var j,old_image;
function change_over(){
var ob=change_over.arguments;
for(i=0;i<document.all.length;i++)
if(document.all[i].name==ob[0]) j=i;
old_image=document.all[j].src;
document.all[j].src=ob[1];
}
function change_out(){
document.all[j].src=old_image;
}
//-->
</script>
</head>
<body bgcolor="#666666">
<table width="800" align="center" cellpadding="0">
<tr>
<td width="9"> </td>
<td width="781" colspan="2"><div align="center"><img src="images1/bar.jpg" width="780" height="120" /></div></td>
</tr>
<tr>
<td> </td>
<td colspan="2"><table width="780" cellpadding="0" cellspacing="0" border="0">
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td width="250"><a href="mp3_control.html" onMouseOut="change_out()" onMouseOver="change_over('Image1','images1/bar_left_thick.jpg')"><img src="images1/bar_left_thick.jpg" width="780" height="37" border="0" name="Image1"></a></td>
</tr>
</table></td>
</tr>
<tr>
<td> </td>
<td colspan="2"> </td>
</tr>
<tr>
<td> </td>
<td colspan="2"><form method="get" action="mp3_control.cgi" name="WEBCAM-TEST">
<div align="center"><span class="STYLE9">點選下面的MP3播放選項,可以選擇MP3播放 </span>
</div>
</form></td>
</tr>
<tr>
<td> </td>
<td colspan="2" align="center"><form method="get" action="mp3_control.cgi" name="LED-TEST">
<div align="left">
<table border="0" width="280" align="center">
<tr>
<td width="133">
<p align="center">選擇播放</td>
</tr>
<tr>
</tr>
<tr>
<td width="140">
<p align="center"> <input type="radio" value="play" checked name="type">播放/暫停</td>
</tr>
<tr>
<td width="140">
<p align="center"><input type="radio" name="type" value="stop" checked>停止播放</td>
</tr>
<tr>
<td width="140">
<p align="center"> <input type="radio" name="type" value="priv">上一歌曲</td>
</tr>
<tr>
<td width="140">
<p align="center"><input type="radio" name="type" value="next">下一歌曲</td>
</tr>
<tr>
<td colspan="2" width="272">
<p align="center"><input type="submit" value="確定(OK)" name="submit"></td>
</tr>
</table>
</div>
<div align="center"></div><div align="center"></div><div align="left"></div><div align="left"></div></form> </td>
</tr>
<tr>
<td height="42"> </td>
<td colspan="2" background="images1/bottom.jpg" height="39" >
<div align="center" class="STYLE13"> <strong>Web Server test page 肖志強製作</strong> <a href="http://embedclub.taobao.com/" class="STYLE14">17768137013 </a></div></td>
</tr>
<tr>
<td> </td>
<td colspan="2" align="center"> </td>
</tr>
</table>
</body>
</html>
而WEB瀏覽器如何將資訊提交給WEB伺服器的呢?博主也會單開文章來講解!