—文章于2022年3月18日0:54重新修改—
-
哈楼,大家好,我又来弄收音机啦,没办法,从事广播技术行业就只能弄这个。
-
本人在地级市电视台工作,主要从事广播传输及发射有关方面的技术工作,我们县区站点较多,因为资金上的问题一直没有一套远程监控及监听的系统,闲(mo)暇(yu)之(shi)余(jian),我就在想能不能自己弄一个简单的监听设备,最开始想通过51来实现,后来发现51可能不行,果断放弃,接着又把目光放到了stm32上,但是因本人水平有限,最终还是不了了之。偶然间看到了树莓派,看介绍,这不就是我想要的baby吗,果断开整。
-
从最开始的c51到后面的stm32,今年在芯片飞涨的时候狠心从js手里买了块树莓派zero 2W,双100网口扩展卡+16g tf卡,一共花了290大洋,闲话不多说了,上干货。
-
以下是我自己业余时间弄的一些简单记录: 项目完整资源地址->点我啦(略微挣点积分)有兴趣的可以看看。
-
思路:5807解调电台后, ffmpeg通过声卡采集并推送到流媒体服务器(树莓派自己就行),远程通过vlc或者web网页播放并控制(tea5767在github上有前辈已经弄好了,我也是参照前辈的一些思路,具体项目地址->https://github.com/LinuxCircle/tea5767。
-
需要的东西:树莓派1个(型号随意,带网口就行),rda5807(某宝2-3块钱一大堆,102bc板子和tea5767通用,tea5767输出只有80mv,需要加放大,而且本身就略贵,最便宜的6块多,rda5807可以直接驱动一般的耳机,像苹果手机的有线耳机实测完全没问题,比较划算),usb声卡10来快钱,或者用ti的pcm2902(2902好像停产了,我在网上买的usb声卡就是2902,怪不得便宜)自己做
-
准备工作:1,我用的python,下载pycharm社区版(这个是免费的!虽然功能不多,够用了),建议在ubuntu下使用,ubuntu软件中心可以直接安装python社区版,win下pip安装模块问题太多。2,树莓派系统安装,进入系统开启i2c,这个一搜一大堆,就不赘述了。3,将5807与树莓派连接好。
-
树莓派:安装ffmpeg,参照->http://relyn.cn/share/47。安装srs,参照->https://blog.csdn.net/weixin_34197488/article/details/91460318。在这里感谢两位前辈。
-
i2c地址确定:首先安装i2c-tools(这个也是一搜一大堆),通过
i2cdetect -y 1
,惊喜来了,3个地址…dump一下,只有0x11有数据就是他了。 -
python:使用的python3.8(本人第一次用python,在b站上2倍速一口气看了播放量最高的那个教程200多集,粗略了解了下,够用了,不涉及高端,有不足的地方欢迎大家指正),pip安装smbus2(i2c用的)和tornado(用到websocket,也可以开启http),或者在pycharm设置里为项目安装。
-
程序设计:导入smbus2,自带了i2c操作相关方法,注意rda5807是16位的寄存器,官方手册上说的是在读取和写入过程中寄存器地址不可见,通过内部自增控制,实测可以直接通过寄存器地址进行读写操作,在读取和写入的时候都是高8位在前,低8位在后
import smbus2
import time
class Rda5807:
def __init__(self):
self.add = 0x11
self.band = 870 #为便于计算,python3的浮点数计算有点奇怪的问题
self.bus = smbus2.SMBus(1)
self.mute_flag = 0
self.on_flag = 0
self.ready_flag = 0
def i2c_read(self, reg_add):
"""
read 2 bytes High byte Low byte
:param reg_add: reg address
:return: 16bit
"""
result = self.bus.read_word_data(self.add, reg_add)
result_l = result >> 8 # Low byte
result_h = (result & 0x00ff) << 8 # High byte
result = result_h + result_l
return result
def i2c_write(self, reg_add, data):
"""
write 2 bytes High byte Low byte
:param reg_add: reg address
:param data: 16bit
"""
data_l = data >> 8 # Low byte
data_h = (data & 0x00ff) << 8 # High byte
data = data_h + data_l
self.bus.write_word_data(self.add, reg_add, data)
其余的控制方法比较简单,参照手册编写就可以了,tornado配置websocket,前端页面采用websocket协议与控制程序通信
from typing import Optional, Awaitable # 这个是根据pycharm对tornado的提示改的,不懂
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import Rda5807
import os
import time
# class MainHandler(tornado.web.RequestHandler):
# def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
# pass
#
# def get(self):
# self.render("rda5807_tornado.html")
def check_push():
"""
check ffmpeg and srs
:return: 0 -> not, 1 -> yes
"""
com_ff = "ps -ef | grep -m 1 ffmpeg | grep -v grep"
com_srs = "ps -ef | grep -m 1 srs | grep -v grep"
ffmpeg_pid = os.popen(com_ff, mode="r").readlines()
srs_pid = os.popen(com_srs, mode="r").readlines()
if len(ffmpeg_pid) and len(srs_pid):
return 1
else:
return 0
def audio_push():
"""
begin audio push
:return: 0 -> stop success,
1 -> start success,
2 -> start failed,
3 -> stop failed
"""
com_push = "/etc/init.d/fmstation "
pid = check_push()
if pid == 0:
os.popen(com_push + "start", mode="r")
time.sleep(10)
if check_push():
print("Audio Push Start Success")
return 1
else:
print("Audio Push Start Failed")
return 2
else:
os.popen(com_push + "stop", mode="r")
time.sleep(10)
if check_push() == 0:
print("Audio Push Stop Success")
return 0
else:
print("Audio Push Stop Failed")
return 3
class Wsocket(tornado.websocket.WebSocketHandler):
controller = None
def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]: # 这里pycharm提示了个什么必须实现所有的什么方法,按照提示改的,不懂
pass
def check_origin(self, origin):
return True
def open(self):
print("connecting...")
try:
self.controller = Rda5807.Rda5807()
# self.controller.set_init()
data = self.info()
self.write_message(data)
except Exception as a:
print(a)
def on_message(self, message):
print("Command", message)
try:
if message == "up":
self.controller.seek(1)
elif message == "down":
self.controller.seek(0)
elif message == "v+":
self.controller.set_volume("+")
elif message == "v-":
self.controller.set_volume("-")
elif message == "mute":
self.mute()
elif message == "on":
self.controller.set_init()
elif message == "off":
self.switch()
# time.sleep(0.2)
elif message == "push":
audio_push()
data = self.info()
self.write_message(data)
except Exception as a:
print(a)
def on_close(self):
print("closing socket")
self.controller = ""
def info(self):
"""
websocket data
:return: data
"""
status = self.controller.get_status()
push_flag = check_push()
data = {"freq": str(self.controller.get_freq()),
"level": self.controller.get_rssi(),
"stereo": "stereo" if status[2] else "mono",
"ready_flag": status[4],
"mute": self.controller.mute_flag,
"push_flag": push_flag,
"vol": str(status[5])}
print(data)
return data
def switch(self):
"""
turn on or off
"""
data = self.controller.get_status()
self.controller.ready_flag = data[4]
if self.controller.ready_flag:
self.controller.power_down()
else:
self.controller.set_init()
def mute(self):
"""
audio out mute
"""
if self.controller.mute_flag:
self.controller.set_mute(0)
else:
self.controller.set_mute(1)
favicon_path = ""
def make_app():
return tornado.web.Application([
# (r'/favicon.ico', tornado.web.StaticFileHandler, {'path': favicon_path}),
# (r"/images/(.*)", tornado.web.StaticFileHandler, {"path": "./images"},),
# (r'/static/(.*)', tornado.web.StaticFileHandler, {'path': "./static"}),
# (r'/', MainHandler),
(r"/ws", Wsocket),
])
if __name__ == "__main__":
app = make_app()
print("WebSockets is Running on port 8888")
app.listen(8888)
tornado.ioloop.IOLoop.instance().start()
我把srs和ffmpeg的启动整合成了一个shell
#!/bin/bash
# /etc/init.d/fmstation
### BEGIN INIT INFO
# Provides: fmstation
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: fm ffmpeg to srs
# Description: none
### END INIT INFO
case "$1" in
start)
echo "Starting fmstation"
sudo /etc/init.d/srs start
sleep 5s
nohup ffmpeg -f alsa -ar 48000 -ac 1 -i hw:1 -f flv rtmp://localhost:1935/live/livestream 1>/dev/null 2>&1 &
;;
stop)
echo "Stopping fmstation"
sudo kill -9 $(ps aux | grep -m 1 'ffmpeg' | awk '{ print $2 }')
sudo kill -9 $(ps aux | grep -m 1 'ffmpeg' | awk '{ print $2 }')
sudo /etc/init.d/srs stop
;;
restart)
echo "Restarting fmstation"
sudo kill -9 $(ps aux | grep -m 1 'ffmpeg' | awk '{ print $2 }')
sudo /etc/init.d/srs restart
sleep 5s
nohup ffmpeg -f alsa -ar 48000 -ac 1 -i hw:1 -f flv rtmp://localhost:1935/live/livestream 1>/dev/null 2>&1 &
;;
esac
exit 0
注意nohup可以指定标准输出和错误输出为null消失掉,不消失掉的话ffmpeg会一直输出到nohub.out。
最后前端使用bootstrap写了个简单页面
下周去县区安装测试。