树莓派 + SSD启动 + CentOS 8 64位 + 内网穿透 + 自动温控风扇 + SMB服务 + 文件分类备份

功能描述

上闲鱼拿下了个树莓派4B 8G,300块,我仿佛听见有人说“哇”?哦,其实它的外显输出有点问题,但我是不关心,真香。

到手之后自然是传统装机,点到为止,64位centos安排起来,smb服务给装上,再整个风扇降降温。后续小功能慢慢安排,请勿期待。

进入主题前说下我过的一个小弯。

本来嘛我是装了NextCloud的,这里简单提一句,我在centos7 x86上是能通过自己找httpd/php/mariadb等模块自己一个个装的,但在树莓派上,因为是arm架构,又用了64位,真心不好找资源,所幸有宝塔(戳我),傻瓜安装,更多具体安装步骤我就不找了,因为头发不多了。

但用了几天,发现它对于个人网盘用户来说,尤其是对于我这种脚本抄写大师,简单性能差到令人无法入眠,直接再见了。我的需求很简单,主要是备份照片,需要时备份几个文件而已,在本文后边我会给出我当前的方案。私人网盘存在当然是有道理的,一个是对于搭建是有点困难但对于使用者来说,还是很简单的,另外就是方便权限管理。

系统安装

先准备好硬件,树莓派4b + 固态硬盘 + 8G+ U盘 + 8G+ TF卡。
然后是操作系统映像,CentOS 8 aarch64系统点我下载,这里就不写怎么烧录了,windows和mac用的工具不一样,都很简单,默认能找centos安装的人都不是小白玩家。

SSD系统安装步骤:戳我
CentOS系统找不到wifi:戳我
nmcli工具的使用:戳我

工具安装

CentOS8无法直接安装screen:戳我
内网穿透方案:戳我
SMB环境搭建:戳我

远程文件管理方案

文件管理

我这部分的就是基于windows或mac的自带远程工具实现连接服务器的SMB实现的,所以使用方法就很简单了,在配置smb服务后,windows使用win + r输入//ip:port/path + 账号密码就能登录,mac是在桌面command + k,输入smb://ip:port/path + 账户密码登录

当然也可以使用SFTP,也就不需要安装SMB服务,我对SMB的需求主要还是远程看漫画(comicscreen只支持smb v1和ftp),之前是放腾讯云上的,确实体验很好。现在嘛还有一个未解决的问题,就是ngrok代理不能直接对SMB进行代理,好像是SMB不仅用了TCP,还用了UDP,所以暂时还没有实现通过本地服务器穿透让ComicScreen看上热乎的漫画。
20201230,已经通过frp看上热乎的漫画了,相对ngrok的方案,缺点就是需要一台有公网的服务器,这点还好,腾讯88一年,还有免费的谷歌羊毛,我都测试过了,腾讯服务器用的重庆的明显比台湾的谷歌延迟低,唉,毕竟是花了钱好。不过说回来,都有公网服务器了,为啥还要内网穿透的哦,这点让我很难受,现在是看在ssh速度明显比ngrok方案好的份儿上,树莓派性能比腾讯那台还是强很多的,只能这样勉强地生活。

照片备份

奈何NextCloud的速度,上传下载简直让人抓狂,所以最后才有了以下脚本,入图片的脚本基本完善了,放文件的我有时间再写起来,目前没有很大的需求,所以没啥进度。
实现需要使用Termux(未测试iOS系统),这个随便百度下就有下载资源,然后至少需要把文件读取权限给它,配置好SSH免密登录,这里也不表,请自行百度,然后以下分别是手机端和服务器端脚本。
配置好之后,在手机端vim .bashrc,然后加入alias pb='sh yourpath/backupfilename.sh’就可以在打开termux时,输入pb自动完成备份,当然也可以自行研究实现打开termux就自动备份(通过.bashrc或…/usr/etc/profile等实现),也可以实现定时备份等花里胡哨的功能。
手机端

#!/bin/bash

# 默认是DCIM文件夹,这个的具体地址要按自己手机的实际位置来修改
# 可以手动传入指定的其他位置
if [[ ${#} -eq 0 ]]; then
	pics_folder="/storage/emulated/0/DCIM/"
elif [[ ${#} -eq 1 ]]; then
	pics_folder=${1}
else
	echo -e "\033[31m脚本只接受一个文件夹参数\033[0m"
	exit 1
fi
# 多个文件传输会重复建立tcp通道,所以先在手机端打包好(未压缩),方便传输
tar_file="/data/data/com.termux/files/home/temp.tar"
tar cf ${tar_file} ${pics_folder}

scp -r ${tar_file} [email protected]:/home/ck/photos/temp/
# 这里使用ssh命令,控制服务器调用python程序进行照片按日期分类
ssh [email protected] "tar xf /home/ck/photos/temp/temp.tar -C /home/ck/photos/temp/; python /root/myProject/myItchat/photo_info_reader/main.py"

rm -f ${tar_file}

服务器端

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: CK
# Date: 2020/12/20
import os
import sys
import time

import exifread

abs_path = os.path.dirname(os.path.abspath(sys.argv[0]))
sys.path.append(os.path.dirname(abs_path))


class PhotoInfoReader:
    def __init__(self):
        self.pics_path = '/home/ck/photos/'
        self.temp_path = '/home/ck/photos/temp/'
        os.makedirs(self.temp_path, exist_ok=True)
        self.file_suffix = ['ani', 'anim', 'apng', 'art', 'bmp', 'bpg', 'bsave', 'cal', 'cin', 'cpc', 'cpt',
                            'dds', 'dpx', 'ecw', 'exr', 'fits', 'flic', 'flif', 'fpx', 'gif', 'hdri', 'hevc',
                            'icer', 'icns', 'ico', 'cur', 'ics', 'ilbm', 'jbig', 'jbig2', 'jng', 'jpg',
                            'jpeg', 'kra', 'mng', 'miff', 'nrrd', 'ora', 'pam', 'pbm', 'pgm', 'ppm', 'pnm',
                            'pcx', 'pgf', 'pictor', 'png', 'psd', 'psb', 'psp', 'qtvr', 'ras', 'rbe', 'sgi',
                            'tga', 'tiff', 'ufo', 'ufp', 'wbmp', 'webp', 'xbm', 'xcf', 'xpm', 'xwd', 'ciff',
                            'dng', 'ai', 'cdr', 'cgm', 'dxf', 'eva', 'emf', 'gerber', 'hvif', 'iges', 'pgml',
                            'svg', 'vml', 'wmf', 'xar', 'cdf', 'djvu', 'eps', 'pdf', 'pict', 'ps', 'swf', 'xaml',
                            'mp4', 'heic']

    @staticmethod
    def info_reader(pic_path):
        """读取图片信息"""
        with open(pic_path, 'rb') as p:
            return exifread.process_file(p)

    @staticmethod
    def move_pic(pic_path, goal_path, color_code):
        """移动图片"""
        pic_path = pic_path.replace('(', '\\(').replace(')', '\\)')
        os.system('mv -f %s %s' % (pic_path, goal_path))
        print('\033[%sm%s\033[0m' % (color_code, pic_path.split('/')[-1]))

    @staticmethod
    def time_info(key, dic):
        """获取图片时间信息"""
        return str(dic[key]).split()[0].split(':')

    @staticmethod
    def date_confirm(string):
        """从文件名中提取日期信息,只考虑两种情况,太复杂"""
        if '_' not in string:
            return None
        date_string = string.split('_')[1]
        if '-' in date_string:
            year = date_string.split('-')[0]
            month = date_string.split('-')[1]
        else:
            year = date_string[: 4]
            month = date_string[4: 6]
        if len(year) == 4 and year[: 2] == '20' and int(month) in range(1, 13):
            return year, month
        else:
            return None

    def run(self):
        # 判断文件夹下是否包含图片,否则删除,不识别图片格式
        # 用于标识文件夹是否存在文件
        pin = 0
        for top, _, files in os.walk(self.temp_path):
            for file in files:
                # 排除db文件
                if file.split('.')[-1].lower() not in self.file_suffix:
                    continue
                pin = 1
                pic_path = os.path.join(top, file)
                tags = self.info_reader(pic_path)
                # 针对heif格式单独处理
                if file.split('.')[-1].lower() == 'heic':
                    date_info = self.time_info('Image DateTime', tags)
                    goal_path = os.path.join(self.pics_path, date_info[0], date_info[1])
                    self.move_pic(pic_path, goal_path, 32)
                elif 'EXIF DateTimeOriginal' in tags.keys():
                    date_info = self.time_info('EXIF DateTimeOriginal', tags)
                    goal_path = os.path.join(self.pics_path, date_info[0], date_info[1])
                    self.move_pic(pic_path, goal_path, 32)
                else:
                    # 这里再针对文件名进行处理,看其是否包含日期
                    date = self.date_confirm(file.split('.')[0])
                    if date:
                        year, month = date
                        goal_path = os.path.join(self.pics_path, year, month)
                        self.move_pic(pic_path, goal_path, 32)
                    else:
                        goal_path = os.path.join('/home/ck/photos/notime')
                        self.move_pic(pic_path, goal_path, 31)
        if not pin:
            print('\033[33m未找到待分类文件\033[0m')
        # 删除空文件夹
        os.system('rm -rf %s; mkdir -p %s' % (self.temp_path, self.temp_path))


def main():
    t = PhotoInfoReader()
    t.run()


if __name__ == '__main__':
    main()

文件备份

这个函数可以直接添加到.bashrc或.bash_profile中,然后就可以通过put + 文件名把文件丢到服务器上了,注意需要修改自己对应的目录,如果需要新建文件夹,可以自己修改逻辑,判断不存在则mkdir -p即可,再把文件丢到新建的文件夹中。

#!/data/data/com.termux/files/usr/bin/bash
# 这里注意上一行,因为下边语法sh应该是不认的,所以需要指定bash的绝对地址,上述地址就是termux的bash位置
# 其它linux系统bash位置一般是/bin/bash
function put() {
    # 得到后缀,建立后缀列表
	local file_name=${1}
    local suffix=`echo ${file_name##*.}| tr 'A-Z' 'a-z'`
    local exe=("exe")
	local document=("doc" "xls" "txt" "docx" "xlsx" "ppt" "pptx" "pdf")
    local apps=("apk")
    local mac=("dmg" "pkg")
    local conf=("conf" "xml" "cfg" "bin")
    local zip=("zip" "tar" "gz" "xz" "rar" "7z" "tgz")
    # 判断文件属于哪类,标记好文件夹名字
	if [[ "${exe[*]}" =~ "${suffix}" ]]; then
		local folder="exe"
	elif [[ "${document[*]}" =~ "${suffix}" ]]; then
		local folder="document"
	elif [[ "${apps[*]}" =~ "${suffix}" ]]; then
		local folder="apps"
	elif [[ "${mac[*]}" =~ "${suffix}" ]]; then
		local folder="mac"
	elif [[ "${conf[*]}" =~ "${suffix}" ]]; then
		local folder="conf"
	elif [[ "${zip[*]}" =~ "${suffix}" ]]; then
		local folder="zip"
	else
		local folder="other"
	fi
    # 手动传参
    if [[ -n ${2} ]]; then
        local folder="${2}"
    fi
    # 判断文件夹是否存在,不存在则返回报错信息
    local final_path="/home/ck/backup/"${folder}
	ssh [email protected] "[[ -d ${final_path} ]]"
    if [[ ${?} -eq 0 ]]; then
		echo -e "\033[32mTargetPath: ${final_path}\033[0m"
        scp ${file_name} [email protected]:${final_path}
        # echo ${final_path}
    else
        echo "文件夹不存在"
    fi
}

alias put='put ${1} ${2}'

温控风扇

最后是自动温控风扇,需求是随着CPU的温度启停、控制转速,PWM模块我直接淘的,样子见下图。

配套的代码见下,在店家的基础上改了点儿,因为我的小破风扇如果不100%运行,噪音比全开声音更大,只配拥有启停……

#!/usr/bin/env python
# encoding: utf-8

import RPi.GPIO
import time

RPi.GPIO.setwarnings(False)
RPi.GPIO.setmode(RPi.GPIO.BCM)
RPi.GPIO.setup(2, RPi.GPIO.OUT)
pwm = RPi.GPIO.PWM(2, 100)
RPi.GPIO.setwarnings(False)

speed = 0
prv_temp = 0

try:
    while True:
        tmpFile = open('/sys/class/thermal/thermal_zone0/temp')
        cpu_temp = int(tmpFile.read())
        tmpFile.close()
        if cpu_temp >= 55000:
            # if prv_temp < 45000:
            #     # 启动时防止风扇卡死先全功率转0.1秒
            #     pwm.start(0)
            #     pwm.ChangeDutyCycle(100)
            #     time.sleep(.1)
            # speed = min(cpu_temp / 125 - 257, 100)
            # 不全速它都有异响,直接干到100就完了
            pwm.start(0)
            pwm.ChangeDutyCycle(100)
        else:
            pwm.stop()
        prv_temp = cpu_temp
        print('\r当前CPU温度:%s' % cpu_temp, end='', flush=True)
        time.sleep(30)
except KeyboardInterrupt:
    pwm.stop()

系统盘扩展

参考这个链接中的第2条:树莓派64位CentOS安装

最后放个完成丑图吧


树莓派 + SSD启动 + CentOS 8 64位 + 内网穿透 + 自动温控风扇 + SMB服务 + 文件分类备份_第1张图片

你可能感兴趣的:(树莓派,内网穿透,centos,arm,shell,python)