本文最后更新于:September 10, 2018 pm
                
              
            
            
              小七比较喜欢在宿舍和室友一起看电影或听音乐,且对画质和音质要求较高,一般都是观看1080P的蓝光REMUX电影(约30G一部)和听无损音质的音乐(30M一首),且观看设备较多(电视盒子、电脑、手机),再加上平时需要下载电影且自己有写博客的习惯,博客和一些其他的重要资料也需要备份,因此决定使用树莓派3B+、一块移动硬盘和一个路由器搭建一个宿舍多媒体中心来满足这些需求。
 
 
1、功能简介 1.1 Samba Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,由服务器及客户端程序构成。SMB(Server Messages Block,信息服务块)是一种在局域网上共享文件和打印机的一种通信协议,它为局域网内的不同计算机之间提供文件及打印机等资源的共享服务。 
换言之,使用Samba可以在局域网内实现文件的共享操作,有些放在移动硬盘里面的文件需要用的时候就不用再插移动硬盘,在网上邻居处拷贝即可。
1.2 miniDLNA DLNA的全称是DIGITAL LIVING NETWORK ALLIANCE(数字生活网络联盟),DLNA并不是创造技术,而是形成一种解决的方案,一种大家可以遵守的规范。 
所以,其选择的各种技术和协议都是当前所应用很广泛的技术和协议。miniDLNA可以实现音乐视频图片的局域网跨设备共享,且目前大多数智能手机、平板和电视均支持DLNA协议,在树莓派上安装miniDLNA服务后即可让处在同一局域网下的设备能轻松访问到树莓派上的影音资源。
1.3 下载机 Transmission全称TransmissionBittorrent,由C开发而成(Mac OS上用的是Objective-C),硬件资源消耗极少,界面极度精简。 支持包括Linux、BSD、Solaris、Mac OS X等多种操作系统,以及Networked Media Tank、WD MyBook、ReadyNAS、D-Link DNS-323 & CH3SNAS、Synology等多种设备。支持GTK+、命令行、Web等多种界面。
Aria2支持Http、FTP、磁力链接和BT下载,可以和Transimission互补。 
1.4 自动备份 数据备份是一个好习惯,但是总是会有遗忘或者是疏漏的情况出现,因此我们可以利用树莓派来实现自动备份。首先可以创建powershell命令实现备份功能,再另存为bat脚本文件,最后利用windows自带的定时任务功能和linux的定时执行命令操作来实现文件自动备份到树莓派上的操作。 
1.5 状态监控 作为长时间运行的多媒体中心,树莓派的运行状态不能忽视,因此我们可以使用LCD1602显示屏 连接树莓派,显示一些必要的信息来监控它的运行状态。(CPU,GPU, RAM, IP, TIME) 
2、安装操作 2.1 Samba 2.1.1 安装ntfs-3g 树莓派接上移动硬盘后,会自动挂载到/media目录下,但是由于我的硬盘是ntfs格式,在树莓系统下只能读不能写,因此我需要安装ntfs-3g服务实现对移动硬盘的写操作,然后再设置开机自动挂载移动硬盘。
sudo apt-get install ntfs-3g sudo mkdir  /home/pi/share
 
sudo umount /dev/sda1 sudo mount -t ntfs-3g /dev/sda1 /home/pi/share sudo chmod  777 /home/pi/share sudo chmod  777 /home/pi
 
sudo vim /etc/fstab /dev/sda1   /home/pi/share  ntfsdefaults0   0
 
2.1.2安装Samba sudo apt-get update sudo apt-get install samba samba-common-bin
 
sudo vim /etc/samba/smb.conf
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 security = share [share]  comment = samba share    path = /home/pi/share    valid user = pi root    public = yes  browseable = yes  writable = yes   
 
sudo smbpasswd -a pi sudo smbpasswd -e pi
 
 sudo /etc/init.d/samba restart
 
 sudo vim /etc/rc.local sudo /etc/init.d/samba restart
 
2.1.3添加网络映射 添加网络映射主要是方便访问,可以将共享的samba文件夹添加到我的电脑中。首先右键我的电脑,点击添加网络映射/Add a network location
输入共享的文件夹路径,然后命名,最后即可完成。
2.2 miniDLNA 2.2.1 安装miniDLNA sudo apt-get update sudo apt-get install minidlna
 
 sudo vim /etc/minidlna.conf media_dir=A,/home/pi/share/Music media_dir=P,/home/pi/share/Picture   media_dir=V,/home/pi/share/Video db_dir=/home/pi/share/dlnadb log_dir=/home/pi/share/dlnalog  
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  sudo mkdir  /home/pi/share/Music sudo mkdir  /home/pi/share/Picture sudo mkdir  /home/pi/share/Video sudo mkdir  /home/pi/share/dlnadb sudo mkdir  /home/pi/share/dlnalog sudo chmod  777 /home/pi/share/Music sudo chmod  777 /home/pi/share/Picture sudo chmod  777 /home/pi/share/Video sudo chmod  777 /home/pi/share/dlnadb sudo chmod  777 /home/pi/share/dlnalog sudo /etc/init.d/minidlna restart sudo /etc/init.d/minidlna status
 
 sudo vim /etc/rc.local sudo /etc/init.d/minidlna restart
 
2.2.2 添加DLNA设备 点击我的电脑左上方的流媒体
系统会自动搜索到局域网中的支持DLNA的设备,点击添加,等待添加完成。
打开支持DLNA或者是流媒体播放的软件,就能看到树莓派中的流媒体文件。
2.3 下载机 2.3.1 安装transmission sudo apt-get update sudo apt-get install transmission-daemon
 
 sudo mkdir  /home/pi/share/Downloads/Incomplete   sudo mkdir  /home/pi/share/Downloads/complete sudo chgrp  debian-transmission /home/pi/share/Downloads/Incomplete sudo chgrp  debian-transmission /home/pi/share/Downloads/complete sudo chmod  777 /home/pi/share/Downloads/Incomplete  sudo chmod  777 /home/pi/share/Downloads/complete 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  sudo vim /etc/transmission-daemon/settings.json"download-dir" : "/home/pi/share/Downloads/complete" ,"incomplete-dir" : "/home/pi/share/Downloads/Incomplete" ,"rpc-whitelist" : "192.168.8.*" , “rpc-username”: “yourname”, “rpc-password”: “yoursecretcode”, sudo service transmission-daemon reload sudo service transmission-daemon restart
 
最后我们访问树莓派的IP再加上9091端口就能登录到下载界面。
2.3.2 安装Aria2 2.3.2.1 安装Aria2 sudo apt-get update sudo apt-get install aria2
 
 sudo mkdir  /etc/aria2 sudo touch  /etc/aria2/aria2.session sudo vim /etc/aria2/aria2.conf
 
dir =/home/pi/share/Downloads disable-ipv6=true   enable-rpc=true   rpc-allow-origin-all=true   rpc-listen-all=true  continue =true   input-file=/etc/aria2/aria2.session  save-session=/etc/aria2/aria2.session  max-concurrent-downloads=3
 
 sudo aria2c --conf-path=/etc/aria2/aria2.conf sudo aria2c --conf-path=/etc/aria2/aria2.conf -D sudo vim /etc/rc.local sudo aria2c --conf-path=/etc/aria2/aria2.conf -D
 
2.3.2.2 安装appache  sudo apt-get install apache2chmod  777 /var/www/html
 
2.3.2.3 安装yaaw 从https://github.com/binux/yaaw下载yaaw,点击右下角的Download  Zip, 下载后将解压后的文件夹内容拷贝到/var/www/html文件夹下。这时在浏览器内输入树莓派的IP,如果出现以下页面,则表示已经正常工作了。
点击左上方的add就可以进行下载。 
2.4 自动备份 2.4.1 创建自动执行文件 使用记事本新建一个文件,里面输入下列代码,然后保存并更改文件名后缀为bat执行文件。
@echo  offecho  Backuping D:\MyBlog\source\_posts--------->192 .168 .8 .106 \share\Backupxcopy  "D:\MyBlog\source\_posts" "\\RASPBERRYPI\share\Backup" /e/I/d/h/r/yexit 
 
其中xcopy指令后两个路径分别为需要备份的文件夹路径和用于存放备份的文件夹路径,其余参数说明如下:
/e:拷贝所有子目录,包括空子目录; 
/I: 如果目标文件或目录不存在且拷贝的文件数多于一,则假设目标为目录; 
/d:只拷贝文件日期与在目标文件后的文件(即修改过的源文件) 
/h:同时拷贝隐藏文件和系统文件 
/r:拷贝并覆盖只读文件 
/y: 复制文件审核设置(不显示已有文件覆盖确认) 
以上参数可以根据需要添加,推荐都加上最好。
2.4.2 设置定时任务 然后我们打开”控制面板”—“计划任务”添加计划任务,计划任务里的执行目标为该批处理文件,设定在什么时候执行则由个人决定。 
执行效果示例如下:
2.5 状态监控 按照针脚跳线连接好LCD1602和树莓派并调节好LCD1602的对比度,使用python编写代码,控制1602输出相关信息,具体代码如下:
将下面这个文件保存为lcd1602.py 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53     from  time import  sleep  class  lcd1602 :   LCD_CLEARDISPLAY= 0x01  LCD_RETURNHOME  = 0x02  LCD_ENTRYMODESET= 0x04  LCD_DISPLAYCONTROL  = 0x08  LCD_CURSORSHIFT = 0x10  LCD_FUNCTIONSET = 0x20  LCD_SETCGRAMADDR= 0x40  LCD_SETDDRAMADDR= 0x80    LCD_ENTRYRIGHT  = 0x00  LCD_ENTRYLEFT   = 0x02  LCD_ENTRYSHIFTINCREMENT = 0x01  LCD_ENTRYSHIFTDECREMENT = 0x00    LCD_DISPLAYON   = 0x04  LCD_DISPLAYOFF  = 0x00  LCD_CURSORON= 0x02  LCD_CURSOROFF   = 0x00  LCD_BLINKON = 0x01  LCD_BLINKOFF= 0x00    LCD_DISPLAYMOVE = 0x08  LCD_CURSORMOVE  = 0x00    LCD_DISPLAYMOVE = 0x08  LCD_CURSORMOVE  = 0x00  LCD_MOVERIGHT   = 0x04  LCD_MOVELEFT= 0x00    LCD_8BITMODE= 0x10  LCD_4BITMODE= 0x00  LCD_2LINE   = 0x08  LCD_1LINE   = 0x00  LCD_5x10DOTS= 0x04  LCD_5x8DOTS = 0x00 
 
      
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 def  __init__ (self, pin_rs=14 , pin_e=15 , pins_db=[17 , 18 , 27 , 22 ], GPIO = None  ):if  not  GPIO:import  RPi.GPIO as  GPIO self.GPIO = GPIO self.pin_rs = pin_rs self.pin_e = pin_e self.pins_db = pins_db   self.GPIO.setmode(GPIO.BCM) self.GPIO.setwarnings(False ) self.GPIO.setup(self.pin_e, GPIO.OUT) self.GPIO.setup(self.pin_rs, GPIO.OUT)  for  pin in  self.pins_db: self.GPIO.setup(pin, GPIO.OUT)   self.write4bits(0x33 )  self.write4bits(0x32 )  self.write4bits(0x28 )  self.write4bits(0x0C )  self.write4bits(0x06 )    self.displaycontrol = self.LCD_DISPLAYON | self.LCD_CURSOROFF | self.LCD_BLINKOFF   self.displayfunction = self.LCD_4BITMODE | self.LCD_1LINE | self.LCD_5x8DOTS self.displayfunction |= self.LCD_2LINE  """ Initialize to default text direction (for romance languages) """  self.displaymode =  self.LCD_ENTRYLEFT | self.LCD_ENTRYSHIFTDECREMENT self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)    self.clear()
 
     
def  begin (self, cols, lines ):  if  (lines > 1 ): self.numlines = lines self.displayfunction |= self.LCD_2LINE self.currline = 0 
 
     
def  home (self ):   self.write4bits(self.LCD_RETURNHOME)  self.delayMicroseconds(3000 ) 
 
     
def  clear (self ):   self.write4bits(self.LCD_CLEARDISPLAY)  self.delayMicroseconds(3000 )
 
     
def  setCursor (self, col, row ):   self.row_offsets = [ 0x00 , 0x40 , 0x14 , 0x54  ]  if  ( row > self.numlines ):  row = self.numlines - 1     self.write4bits(self.LCD_SETDDRAMADDR | (col + self.row_offsets[row]))
 
     
def  noDisplay (self ): """ Turn the display off (quickly) """    self.displaycontrol &= ~self.LCD_DISPLAYON self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
 
     
def  display (self ):""" Turn the display on (quickly) """    self.displaycontrol |= self.LCD_DISPLAYON self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
 
     
def  noCursor (self ):""" Turns the underline cursor on/off """    self.displaycontrol &= ~self.LCD_CURSORON self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
 
     
def  cursor (self ):""" Cursor On """    self.displaycontrol |= self.LCD_CURSORON self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
 
     
def  noBlink (self ):""" Turn on and off the blinking cursor """    self.displaycontrol &= ~self.LCD_BLINKON self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
 
     
def  noBlink (self ):""" Turn on and off the blinking cursor """    self.displaycontrol &= ~self.LCD_BLINKON self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
 
     
def  DisplayLeft (self ):""" These commands scroll the display without changing the RAM """    self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVELEFT)
 
     
def  scrollDisplayRight (self ):""" These commands scroll the display without changing the RAM """    self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVERIGHT);
 
     
def  leftToRight (self ):""" This is for text that flows Left to Right """    self.displaymode |= self.LCD_ENTRYLEFT self.write4bits(self.LCD_ENTRYMODESET | self.displaymode);
 
     
def  rightToLeft (self ):""" This is for text that flows Right to Left """  self.displaymode &= ~self.LCD_ENTRYLEFT self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)
 
     
def  autoscroll (self ):""" This will 'right justify' text from the cursor """    self.displaymode |= self.LCD_ENTRYSHIFTINCREMENT self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)
 
     
def  noAutoscroll (self ): """ This will 'left justify' text from the cursor """    self.displaymode &= ~self.LCD_ENTRYSHIFTINCREMENT self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)
 
     
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 def  write4bits (self, bits, char_mode=False  ):""" Send command to LCD """    self.delayMicroseconds(1000 )    bits=bin (bits)[2 :].zfill(8 )   self.GPIO.output(self.pin_rs, char_mode)  for  pin in  self.pins_db: self.GPIO.output(pin, False )  for  i in  range (4 ):if  bits[i] == "1" : self.GPIO.output(self.pins_db[::-1 ][i], True )   self.pulseEnable()  for  pin in  self.pins_db: self.GPIO.output(pin, False )  for  i in  range (4 ,8 ):if  bits[i] == "1" : self.GPIO.output(self.pins_db[::-1 ][i-4 ], True )   self.pulseEnable()
 
     
def  delayMicroseconds (self, microseconds ): seconds = microseconds / float (1000000 )  sleep(seconds)
 
     
def  pulseEnable (self ): self.GPIO.output(self.pin_e, False ) self.delayMicroseconds(1 )    self.GPIO.output(self.pin_e, True ) self.delayMicroseconds(1 )    self.GPIO.output(self.pin_e, False ) self.delayMicroseconds(1 )   
 
     
def  message (self, text ):""" Send string to LCD、Newline wraps to second line"""   for  char in  text:if  char == '\n' : self.write4bits(0xC0 ) else : self.write4bits(ord (char),True )
 
     
if  __name__ == '__main__' :   lcd = lcd1602() lcd.clear() lcd.message("hello world!" )
 
再将这个文件保存为1602.py 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51   from  lcd1602 import  *from  datetime import  *import  commands  def  get_cpu_temp (): tmp = open ('/sys/class/thermal/thermal_zone0/temp' ) cpu = tmp.read() tmp.close()return  '{:.2f}' .format ( float (cpu)/1000  ) + ' C'   def  get_gpu_temp (): tmp = commands.getoutput('vcgencmd measure_temp|awk -F= \'{print $2}\'' ).replace('\'C' ,'' ) gpu = float (tmp)return  '{:.2f}' .format ( gpu ) + ' C'   def  get_time_now ():return  datetime.now().strftime('%H:%M:%S\n   %Y-%m-%d' )  def  get_ip_info (): ip= commands.getoutput('ifconfig eth0 | grep inet | awk \'{ print $2 }\' | awk \'NR==1\'' )return  'Ethernet IP:\n'  + ip   def  get_mem_info (): total= commands.getoutput('free -m|grep Mem:|awk \'{print $2}\'' )   free= commands.getoutput('free -m|grep Mem:|awk \'{print $4}\'' )return  'MEM:\n'  + free +'/' + total +'M'    lcd = lcd1602() lcd.clear()  if  __name__ == '__main__' :  while (1 ): lcd.clear() lcd.message( get_ip_info() ) sleep(5 )   lcd.clear() lcd.message( get_time_now() ) sleep(5 )   lcd.clear() lcd.message( get_mem_info() ) sleep(5 )   lcd.clear() lcd.message( 'CPU: '  + get_cpu_temp()+'\n'  ) lcd.message( 'GPU: '  + get_gpu_temp() ) sleep(5 )
 
最后将两个文件保存到同一个目录下面,然后编辑文件设置开机启动即可让LCD1602循环显示信息。 
我将这两个文件保存到/home/pi/1602目录下 
 sudo vim /etc/rc.local sudo python /home/pi/1602/1602.py
 
3、爬虫服务器 3.1 Python源码示例 from  urllib import  requestimport  jsonimport  timefrom  datetime import  datetimefrom  datetime import  timedeltaimport  pandas as  pdfrom  lxml import  etreefrom  tqdm import  tqdmimport  randomimport  reimport  csv
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 def  get_data (url ): headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36'  } req = request.Request(url, headers=headers) response = request.urlopen(req)if  response.getcode() == 200 :return  response.read()return  None def  parse_data (html ): data = json.loads(html)['cmts' ]   comments = []for  item in  data: comment = {'id' : item['id' ],'cityName' : item['cityName' ] if  'cityName'  in  item else  '' ,  'content' : item['content' ].replace('”' ,' ' ).replace('“' ,' ' ).replace(',' ,' ' ).replace(',' ,' ' ).replace('\n' , ' ' ),  'score' : item['score' ],'startTime' : item['startTime' ] } comments.append(comment)return  commentsdef  savetoCSV (): start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S' )   end_time = '2015-05-01 00:00:00' while  start_time > end_time: url = 'http://m.maoyan.com/mmdb/comments/movie/248170.json?_v_=yes&offset=0&startTime='  + start_time.replace(' ' , '%20' ) html = None ''' 问题:当请求过于频繁时,服务器会拒绝连接,实际上是服务器的反爬虫策略 解决:1.在每个请求间增加延时0.1秒,尽量减少请求被拒绝  2.如果被拒绝,则0.5秒后重试 ''' try : html = get_data(url)except  Exception as  e: time.sleep(0.5 ) html = get_data(url)else : time.sleep(0.1 ) comments = parse_data(html)print (comments) start_time = comments[14 ]['startTime' ]   start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S' ) + timedelta(seconds=-1 )   start_time = datetime.strftime(start_time, '%Y-%m-%d %H:%M:%S' )  
 
    
for  item in  comments:with  open ('test3.csv' , 'a' , encoding='utf-8' ) as  f: f.write(str (item['id' ]) + ','  + item['startTime' ].strip('[\'' ).split(' ' )[0 ] + ','  + str (item['score' ]) + ','  + item['cityName' ] + ','  + str (item['content' ]) + '\n' )
 
     
if  __name__ == '__main__' : html = get_data('http://m.maoyan.com/mmdb/comments/movie/248170.json?_v_=yes&offset=0&startTime=2018-12-31%2022%3A25%3A03' ) comments = parse_data(html)print (comments) savetoCSV()
 
3.2 效果展示 
4、写在最后 重启之后就可以尽情地享用树莓派打造的多媒体中心了,虽然树莓派3B+只有USB2.0接口,但是只要搭配上百兆lan口的路由器,局域网内流畅观看40G左右大小的一部电影还是毫无问题的。 
如果有更高的需求,还是建议上更好的路由器和NAS吧。