Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
柿饼_PersimmonUI
柿饼_PersimmonUI
柿饼_PersimmonUI
【12月原创】柿饼UI作业(四)—— 网络流媒体播放器
发布于 2020-12-28 15:09:53 浏览:936
订阅该版
[tocm] # Persimmon UI作业(四)—— 网络流媒体播放器 > 前言:这次的作业花费了一周的时间,因为学校接近末考同时面对大连疫情的严峻,使整个开发流程变得断断续续,不过还好总算是完成了这次网络流媒体播放器,总体来说还是有些难度的,对我来说也算是个人水平的进一步的提升了。为此记录一下开发流程以及我的创意分享。 视频演示效果: [视频演示效果](https://www.bilibili.com/video/BV1Lf4y1C7Jz) ## 一.项目的规划/构思 > 因为柿饼派的IO口有限,外接的IO口除了两个串口就是电源和地了,但只要有串口基本上就可以满足我们外接设备的需求了,毕竟是用柿饼派作为开发的SOC。 网络流媒体播放器的整个系统的框架如下: ![系统框架](https://oss-club.rt-thread.org/uploads/20220714/ca05f8a7cfcdf2c88b5bf2c5c46530681beac9c0.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) * 因为我们需要获取网络音频等数据,所以联网的模块是必不可少的,良心的是柿饼派上板载SPI WIFI模块 (RW007)通讯速度就叫一个快! - 获取到的音频需要播放出来,柿饼PI的Audio Player 接口可以播放网络/本都音频流。 - 同时通过HTTP可以获取API接口的天气/音乐数据,MQTT可以实现柿饼派订阅/发布指定topic,可以实现一个智能物联网屏幕的功能。 - UART接口可以和其他单片机进行数据交互,这里我使用了柿饼派与小熊派进行数据的联动,通过小熊派NB模组将数据上报到小熊派订阅的topic,同时通过串口与柿饼派连接,通过串口将数据发送至柿饼派。这样就把我们前两节课所学习到的网络/串口结合到了一起,岂不美哉! ## 二.项目的实现 对于整个项目可以分为三个部分: * 1.在app.js中编写WIFI的配网-入网-获取网络信息的逻辑。 * 2.在app.js将UART作为全局对象,在子page中实现app.js的回调函数。 * 3.在子page中通过设置UI控件来绑定列表、button等控件,通过控件将网络获取的数据以UI界面展示。 ### 1.WIFI配网-入网的实现: 整个WIFI调用流程如下: * 1.实例化WIFI对象pm.createwifi()。 * 2.扫描WIFI热点信息scan()。 * 3.连接WIFI热点connect() 连接特定网络。 * 4.监听连接事件,回调onConnectEvent判断连接结果。 * 5.监听WFI连接网络,回调onNetworkEvent判断连接结果,当返回为true ,则连接网络成功。 ![wifi-1、2步骤](https://oss-club.rt-thread.org/uploads/20220714/27f3e0b1204db9ba54302aeb1bec5859459ed687.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) 监听连接事件,回调onConnectEvent判断连接结果: ![监听连接事件,回调onConnectEvent判断连接结果](https://oss-club.rt-thread.org/uploads/20220714/8acbe0fca331394e9fc73b9a99b325df7d174cd0.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) 监听连接网络,回调onNetworkEvent判断连接结果: ![onConnectEvent](https://oss-club.rt-thread.org/uploads/20220714/989cd55680c4064aa80e7a82f678d978d462ad34.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) 发起HTTP—GET请求消息: ![发起HTTP请求](https://oss-club.rt-thread.org/uploads/20220714/9d2a5ea425fe672eaf232c476aebafbca1a31066.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) ### 2.串口收发功能的实现: 1.设置全局接收: ![串口接收](https://oss-club.rt-thread.org/uploads/20220714/b444f7e9a5b7110a16ec65b03e92d253213fdd5d.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) 2.在子page设置接受回调: ![串口](https://oss-club.rt-thread.org/uploads/20220714/dff2377bcd08af95d882b205b408ed75299d871e.png) ![接收回调](https://oss-club.rt-thread.org/uploads/20220714/d7f49c4e7c83ef04fc04f2f4dba37887490d7f0e.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) ### 3.数据在UI界面的展示以及音乐播放: 让我们先看一下整体的UI设计布局: 顶部:button+label+imagebox控件。 中间:Listctrl的控件用于展示MV列表以及网络专辑图片列表。 ![UI](https://oss-club.rt-thread.org/uploads/20220714/4b63e3ce5d76fcb0edc58769fa2acffe4260688b.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) ![VideoItem](https://oss-club.rt-thread.org/uploads/20220714/7cd355eddf5f2ddae2805ead001f61ec864569ee.png) > 那么js代码中是怎么实现将网络数据绑定到UI控件,播放视频以及音乐的呢? 1.定义存储视频的数组: ![视频数组](https://oss-club.rt-thread.org/uploads/20220714/433ee93083def1723cae578c247a340cd31fcc5e.png) 2.绑定每一条Videoltem中的label,button, imagebox属性(类似安卓中的Adapter),将每一条通过列表展示出来 (类似安卓中的RecyclerView): ![绑定数据到列表](https://oss-club.rt-thread.org/uploads/20220714/34f58210eec20e83ed5df1fb9323f6e93aa9922c.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) 3.fmData是我们在app.js中定义的数据,用于存放通过HTTP请求到的JSON数据。 ![fmData](https://oss-club.rt-thread.org/uploads/20220714/7f41823354173d6004f50521f44210c4b58b75a6.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) 具体JS代码如下: ```javascript /*************Music Panel*****************/ loadAlbum: function (Class) { if (fmData == 0) return; var that = this; /***加载music list***/ { var MusicList = new Array(); for (var i = 0, len = fmData.Music.length; i < len; i++) { var item = new Object(); item.albumName = new Object(); item.albumName.value = fmData.Music[i].albumName; var imageArr = fmData.Music[i].albumImage.split("/"); item.albumImage = new Object(); item.albumImage.norImg = imageArr[imageArr.length - 1];//图片 item.albumImage.customProperty = fmData.Music[i]; //MUSIC整个json console.log("@@===========>mp3-src:" + fmData.Music[0].albumList[0].src); item.albumImage.id = "Music" + i; MusicList.push(item); } this.setData({ Music_list: { list: { page: that, items: [{ xml: "Panels/AlbumItem", items: MusicList }] } } }); } }, ``` 音乐播放器设计界面如下: ![UI](https://oss-club.rt-thread.org/uploads/20220714/c345512918fec2309153a7538312d7ef05f83c53.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) > 通过暂停/播放等按钮可以实现播放以及切换歌曲功能 > > 播放音乐同时通过prograssbar控件展示播放进度,slider控件控制播放音量的大小。 > > 点击上/下一首歌曲,底部列表回进行相应滚动。 具体实现的JS代码如下: ```javascript onLoad: function (event) { refreshDate(this); //获取信号质量--app.js this.audioEvenDeinit(); this.albumData = event; this.urlList = new Array(); //存放音乐数组 var imageArr = this.albumData.albumImage.split("/"); this.setData({ albumImage: imageArr[imageArr.length - 1] }); this.setData({ albumInfo: this.albumData.albumName }); this.setData({ volume_slider: { value: audio.getVolume() } }); var listItems = new Array(); for (var i = 0, len = this.albumData.albumList.length; i < len; i++) { var item = new Object(); item.label1 = new Object(); item.panel1 = new Object(); item.panel1.id = "musicPanel" + i; //设置每一个列表前的图标 item.imagebox1 = new Object(); item.imagebox1.id = "musicImage" + i; //设置每一个列表前的图标id item.label1.value = this.albumData.albumList[i].trackName;//每一条音乐名称 console.log("@@=======================TrackName======================="); console.log("@@=====>trackName is:" + this.albumData.albumList[i].trackName);//音乐名称 /*** 路径 ***/ this.urlList.push(this.albumData.albumList[i].src); console.log("@@=======================AlbumList_src======================="); console.log("@@=====>AlbumList is:" + this.albumData.albumList[i].src);//mp3文件 listItems.push(item); } this.setData({ List: { list: { page: this, items: [{ xml: "Panels/PlayListItem", items: listItems }] } } }); this.audioEventInit(); }, ``` ![audio](https://oss-club.rt-thread.org/uploads/20220714/a6bcc75349faebd14f1c338aa52c4462ca15c931.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) 播放,暂停的具体JS代码如下: ```javascript //播放音乐 playMusic: function () { if (this.isPlay == false) { this.isPlay = true; this.setData({ Switch1: { value: true } }) } if (this.playIndex < 0) { this.playIndex = this.urlList.length - 1; } else if (this.playIndex >= this.urlList.length) { this.playIndex = 0; } // set url var url = this.urlList[this.playIndex]; audio.stop(); audio.setSrc(url); if (this.isPlay && wifi.isConnected) { audio.play(); } else { this.isPlay = false; this.setData({ Switch1: { value: false } }); } }, //暂停/播放--按钮回调 onSwitch: function (event) { this.isPlay = event.detail.value; if (this.isPlay) { this.playMusic(); } else { audio.pause(); } }, ``` --- 说完了音乐播放,那么天气数据的获取以及语音播报是怎么实现的呢? 首先让我们看一下天气page的UI界面吧: ![weather](https://oss-club.rt-thread.org/uploads/20220714/daf4b1b7bef7b8fed66239294b4fdd2c5195ab5b.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) JS代码实现了通过HTTP获取到的json数据绑定在UI控件上面: ```javascript loadWeatherInfo: function () { if (weatherInfo == 0) { //没有请求成功 return; } this.setData({ currentTempeature_label: { value: weatherInfo.hourly.Temperature[0].value } }); switch (weatherInfo.hourly.Skycon[0].value) { case "CLEAR_DAY": case "CLEAR_NIGHT": this.setData({ weather_img: { value: "clear_white.png" } }) this.setData({ weather_label: { value: "晴" } }) break; case "PARTLY_CLOUDY_DAY": case "PARTLY_CLOUDY_NIGHT": this.setData({ weather_img: { value: "cloudy_white.png" } }) this.setData({ weather_label: { value: "多云" } }) break; case "CLOUDY": this.setData({ weather_img: { value: "overcase_white.png" } }) this.setData({ weather_label: { value: "阴" } }) break; case "WIND": this.setData({ weather_img: { value: "windy_white.png" } }) this.setData({ weather_label: { value: "大风" } }) break; case "HAZE": this.setData({ weather_img: { value: "haze_white.png" } }) this.setData({ weather_label: { value: "雾霾" } }) break; case "RAIN": this.setData({ weather_img: { value: "rain_white.png" } }) this.setData({ weather_label: { value: "雨" } }) break; case "SNOW": this.setData({ weather_img: { value: "snow_white.png" } }) this.setData({ weather_label: { value: "雪" } }) break; } this.setData({ humidity_label: { value: parseInt(weatherInfo.hourly.Humidity[0].value * 100) + "%" } }) this.setData({ weatherInfo_panel: { refresh: true } }); }, ``` GET请求,将上一步我们获取到的天气数据拼接成链接,再次发送请求至百度语音合成API接口,就实现了点击按钮播报天气: ```javascript //点击按钮语音播报 onBroadcast: function (event) { var str = currentCity.name; console.log("weatherInfo======>" + weatherInfo.hourly.Skycon[0].value) switch (weatherInfo.hourly.Skycon[0].value) { case "CLEAR_DAY": case "CLEAR_NIGHT": str += "今天天气晴朗" break; case "PARTLY_CLOUDY_DAY": case "PARTLY_CLOUDY_NIGHT": str += "今天多云" break; case "CLOUDY": str += "今天阴天" break; case "WIND": str += "今天有风" break; case "HAZE": str += "今天有雾霾" break; case "RAIN": str += "今天下雨" break; case "SNOW": } str += (",气温为" + weatherInfo.hourly.Temperature[0].value + "度,相对湿度为百分之" + parseInt(weatherInfo.hourly.Humidity[0].value * 100) + "。"); var that = this; var http = require('http'); http.request({ url: 'http://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=uGmHtBFMOkme2XeGWzKLREDv&client_secret=xG7I0fy4GQLPpLqanq7pZAGtNXGUW564', success: function (res) { var jnews = res.data.jsonParse();//获取access_token值 var url = "http://tsn.baidu.com/text2audio?tex=" + str + "&lan=zh&cuid=44-b2-95-27-49-1f&ctp=1&per=4&tok=" + jnews.access_token; http.request({ url: url, header: { "Content-Type": "application/json" }, success: function (res) { console.log('request success'); var file = pm.getFileSystemManager(); file.writeFile({ filePath: "forecast.mp3", data: res.data, success: function () { audio.stop(); audio.setSrc("/mnt/sd0/filesystem/forecast.mp3"); audio.play(); }, complete: function () { console.log("writeFile complete"); } }); }, }) } }) }, ``` > 通过上面的积累,这样的一个网易云界面UI就出来了,网易云有的咱们也不能差(哈哈,来对比下网易云和柿饼UI。 ![网易云界面](https://oss-club.rt-thread.org/uploads/20220714/6aa0a9d884c3f03d5ab618e403d8b4010a445ac8.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) ![仿网易云播放器](https://oss-club.rt-thread.org/uploads/20220714/41201612164c9080a96dec04e20cab41c6da7dd8.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70)![歌手](https://oss-club.rt-thread.org/uploads/20220714/143b35c49d041a50c14a932eb01602451b8b3f2e.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) ![我的音乐](https://oss-club.rt-thread.org/uploads/20220714/114b63c38fa6721428d974cd2e4d1d24eb646969.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70)![仿网易云播放](https://oss-club.rt-thread.org/uploads/20220714/230705368233a5e62c0396f0108da635cb28fba0.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNjEwNDMw,size_16,color_FFFFFF,t_70) > 以上就是整个项目的分享,总体来说很有挑战性创新性的一次作业,当真正做出来之后发现很有成就感。同时十分感谢柿饼UI的平台给了我们这么便捷开发的工具,以最短的时间最简单的方式区实现一个小项目。对于我个人而言意义很是重大,我很庆幸在当初迷茫如何去选择一个能够快速上手的嵌入式屏幕时我发现了**Persimmon UI**。时长1个月的柿饼UI的学习结束啦,感谢群里各位老师以及各位大佬的帮助与指导。未来我会使用柿饼做一些更加有趣的项目,同时开源在我的git以及RTT社区上面。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
Rb君
这家伙很懒,什么也没写!
文章
23
回答
107
被采纳
11
关注TA
发私信
相关文章
1
【PersimmonUI柿饼学习营】+ A Byte of China + 环境搭建&20W年薪广告
2
【柿饼学习营】+werrysuzhen+20W年薪作业
3
【PersimmonUI柿饼学习营】+ meetwit + 20W年薪广告&lesson01
4
【PersimmonUI柿饼学习营】+ DaZhou + 20W年薪作业+视频有彩蛋
5
【PersimmonUI柿饼学习营】+ DMY+ lesson01 20W年薪广告
6
【PersimmonUI柿饼学习营】+ Bigmagic+20W年薪广告&day01
7
【PersimmonUI柿饼学习营】+ chowguohua+年薪广告
8
【柿饼学习营】+werrysuzhen+脚本及事件学习
9
【PersimmonUI柿饼学习营】+ DaZhou+ 手把手教你做计算器
10
【PersimmonUI柿饼学习营】+ A Byte of China + 2048游戏
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
YZRD
2
篇文章
3
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部