meishild 发表于 2019-8-22 22:16:34

篇七:树莓派zero w蓝牙网关

本帖最后由 meishild 于 2019-8-26 11:23 编辑

目录地址:https://bbs.hassbian.com/thread-8041-1-1.html


# 篇七:树莓派zero w蓝牙网关

因为大家强烈需求,我优先先写树莓派蓝牙网关把。

我选的是树莓派zero w,主要是第一个小巧非常小。并且能提供蓝牙、wifi。



也可以选择焊接好阵脚,毕竟这个也支持gpio的数据通信,接个433+红外就是全面网关。


## 系统安装
先去下载树莓派官方的镜像,因为zero的cpu比毕竟比较若不想在上面跑什么高负载的东西,所以我直接选择,没有桌面环境的只有命令行的版本。 `Raspbian Buster Lite` ,[下载地址](https://www.raspberrypi.org/downloads/raspbian/) ,下载好2.2个G。
然后写入到mini sd卡的镜像。

- 可以用工具,我是mac os所以我使用 `balenaEtcher`



- 当然也可以直接dd写入。

以下mac命令,和linux完全不同,linux使用fdisk
```yaml
diskutil list
```


```yaml
dd if=2019-07-10-raspbian-buster-lite.img of=/dev/disk3 bs=10m
```


#### 配置wifi

因为安装的没有图形界面,同样的配置wifi连接比较麻烦,所以官方提供了一种方案直接写wifi配置。

新建一个配置文件 `wpa_supplicant.conf` ,内容如下,可以配置多个network。

> 注意树莓在zero w仅支持2.4Gwifi网络。


```yaml
country=CN
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev

update_config=1

network={
ssid="WiFi-A"
psk="12345678"
key_mgmt=WPA-PSK
priority=1
}

network={
ssid="WiFi-B"
psk="12345678"
key_mgmt=WPA-PSK
priority=2
scan_ssid=1
}
```
**#ssid:网络的ssid**
**#psk:密码**
**#priority:连接优先级,数字越大优先级越高(不可以是负数)**
**#scan_ssid:连接隐藏WiFi时需要指定该值为1**

如果你的 WiFi 没有密码

```yaml
network={
ssid="你的无线网络名称(ssid)"
key_mgmt=NONE
}
```

如果你的 WiFi 使用WEP加密

```yaml
network={
ssid="你的无线网络名称(ssid)"
key_mgmt=NONE
wep_key0="你的wifi密码"
}
```

如果你的 WiFi 使用WPA/WPA2加密

```yaml
network={
ssid="你的无线网络名称(ssid)"
key_mgmt=WPA-PSK
psk="你的wifi密码"
}
```

然后把这个文件`wpa_supplicant.conf` 复制到写好的sd卡内,写入成功以后拔出重新插入就会出现一个boot的u盘,复制到里面。


#### 配置SSH

因为没有屏幕,没有键盘,所以需要一个能连接进去的方法,而且树莓派的系统又默认没有开启ssh,所以新建一个文件。

同样在 boot 分区新建一个文件,空白的即可,文件命名为 ssh。注意要小写且不要有任何扩展名。
树莓派在启动之后会在检测到这个文件之后自动启用 ssh 服务。随后即可通过登录路由器找到树莓派的 IP 地址,通过 ssh 连接到树莓派了。

> windows特别注意,因为windows默认保存的文件都是有后缀的,这个完全需要把后缀删除。


然后把SD卡插回去,并通电。


## 配置树莓派


#### 连接树莓派

首先通过路由器查看树莓派的ip,因为只要连接了网络总要分配ip地址,在路由器上会出现一个新的设备。



能ping通说明已经联网好了。

现在ssh过去。 `ssh [email protected]` ,密码是:raspberry,地址换成你自己的,用户名和密码是固定的。


#### 初始化基础配置

`sudo raspi-config` 修改系统配置,密码就是ssh的密码。



要改的有几项,

- 第一个把密码改了。
- 第四个修改地区,
- 第一天修改语言,我会修改成 `zh_CN.UTF8` ,确定以后要等好一会。
- 然后修改时区,改成亚洲上海。
- 然后键盘类型,改成chinese。


#### 设置root登陆

- 设置root密码: `sudo passwd root`
- 解锁root密码登陆: `sudo passwd --unlock root`
- 设置ssh登陆: `sudo nano /etc/ssh/sshd_config`

```yaml
Ctrl + W 快捷键 搜索 PermitRootLogin without-password
在下一行增加PermitRootLogin yes,注意不能#号注释。
Ctrl + O 快捷键 保存
Ctrl + O 快捷键 退出 Nano 编辑器
```

- 然后重启树莓派: `sudo reboot`
- 使用root登陆: `ssh root@pi_host`

**注意:为什么要改成root,网关是需要通过系统调度的,有一些比如蓝牙搜索是root权限,所以通过root账号才能进行python配置,当然也可以在系统内 `sudo su` 方式切换成root。**


#### 修改更新源

**因为我都是root用户所以都不加sudo了。**

默认的源在国外那速度简直了。

```yaml
nano /etc/apt/sources.list
```

可以全删掉,也可以在前面加上#号注释然后增加源。

```yaml
deb http://mirrors.ustc.edu.cn/raspbian/raspbian/ buster main contrib non-free rpi
```
Ctrl + O
Ctrl + O
保存并退出。

```yaml
sudo nano /etc/apt/sources.list.d/raspi.list
```
这个全注释掉或者删除就好了。


#### 更新库,并安装需要的组件

> 有同学一直装不上bluepy,完整按照我操作我保你成功。



`apt update`

```yaml
# 更新本地缓存源
apt update

# 安装python3 pip
apt install python3-pip -y

# 安装git
apt install git -y

# 安装bluepy依赖
apt install python-pip libglib2.0-dev -y

# 安装mqtt客户端,方便查看mqtt消息
apt install mosquitto-clients -y

# 安装vim,我习惯用这个
apt install vim -y

# 安装bluepy
pip3 install bluepy
pip3 install btlewrap
```

修改pip的源否则这货安装也巨慢:
esc是按键不是敲的。。。

```yaml
mkdir ~/.pip
vim pip.conf
esc
:set paste
i
```

换成阿里的。

```yaml

timeout = 10 # 超时
index-url = http://mirrors.aliyun.com/pypi/simple/ # 第一源
index-index-url = http://pypi.douban.com/simple/ # 第二

trusted-host =
  mirrors.aliyun.com
  pypi.douban.com
```

然后esc输入:wq保存并退出。


## 测试蓝牙

协议个bluepy蓝牙扫描测试以下。
编写一个文件vim、nano都行,我习惯vim

```yaml
vim scan_bt.py
esc
:set paste
i
```

然后粘贴代码,crtl+v:

```yaml
from btlewrap import BluepyBackend

def scan(backend, timeout=10):
    """Scan for miflora devices.

    Note: this must be run as root!
    """
    result = []
    for (mac, name) in backend.scan_for_devices(timeout):
      print("%s --> %s" % (name, mac))
    return result


if __name__ == '__main__':
    print("START_SCAN")
    scan(BluepyBackend)
    print("SCAN_FINISH")
```
然后esc输入:wq保存并退出。

然后执行扫描: `python3 scan_bt.py`



这就ok了,蓝牙这几个地址其实我用过的是有规律的。

- 68:3e:34 开头的这个都是魅族遥控器的。
- c4:7c:8d 开头的都是花花草草的。
- 58:2D:34 开头的都是小米那个蓝牙温度计的。


## 备份树莓派

中间插一个步骤,备份树莓派,可以是备份安装好的,也可以随时备份,因为我的sd卡是32g的如果用普通的dd命令制作的img镜像会高达32G。
网上找了好几天终于找到一个脚本不需要其他的,只要通过脚本并插入u盘,就可以制作一个最小img镜像,后面可以快速恢复。

脚本是:

```bash
#!/bin/sh

echo ===================== part 1, install tools ===============================
sudo apt-get -y install rsync dosfstools parted kpartx exfat-fuse pv

echo ===================== part 2, choose USB ===============================
#mount USB device
usbmount=/mnt
mkdir -p $usbmount
if [ -z $1 ]; then
      echo "no argument, assume the mount device is /dev/sda1 ? Y/N"
      read key
      if [ "$key" = "y" -o "$key" = "Y" ]; then
                sudo mount -o uid=1000 /dev/sda1 $usbmount
      else
                echo "$0 , e.g. $0 /dev/sda1"
                exit 0
      fi
else
      sudo mount -o uid=1000 $1 $usbmount
fi
if [ -z "`grep $usbmount /etc/mtab`" ]; then
      echo "mount fail, exit now"
      exit 0
fi

img=$usbmount/rpi-back-`date +%Y%m%d-%H%M`.img
echo "备份文件位置:$img"

echo ===================== part 3, create a new blank img ===============================
# New img file
#sudo rm $img
bootsz=`df -P | grep /boot | awk '{print $2}'`
rootsz=`df -P | grep /dev/root | awk '{print $3}'`
totalsz=`echo $bootsz $rootsz | awk '{print int(($1+$2)*1.3)}'`
echo "start create img,total size=${totalsz}K"
#sudo dd if=/dev/zero of=$img bs=1K count=$totalsz
pv -tpreb /dev/zero |sudo dd of=$img bs=1K count=$totalsz

# format virtual disk
bootstart=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p1 | awk '{print $2}'`
bootend=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p1 | awk '{print $3}'`
rootstart=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p2 | awk '{print $2}'`
echo "format virtual disk:    boot: $bootstart >>> $bootend, root: $rootstart >>> end"
#rootend=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p2 | awk '{print $3}'`
sudo parted $img --script -- mklabel msdos
sudo parted $img --script -- mkpart primary fat32 ${bootstart}s ${bootend}s
sudo parted $img --script -- mkpart primary ext4 ${rootstart}s -1
loopdevice=`sudo losetup -f --show $img`
device=/dev/mapper/`sudo kpartx -va $loopdevice | sed -E 's/.*(loop)p.*/\1/g' | head -1`
sleep 5
sudo mkfs.vfat ${device}p1 -n boot
sudo mkfs.ext4 ${device}p2
echo "format finish"

echo ===================== part 4, fill the data to img =========================
# mount partitions
mountb=$usbmount/backup_boot/
mountr=$usbmount/backup_root/
mkdir -p $mountb $mountr
# backup /boot
sudo mount -t vfat ${device}p1 $mountb
sudo cp -rfp /boot/* $mountb
sync
echo "...Boot partition done"
# backup /root
sudo mount -t ext4 ${device}p2 $mountr
if [ -f /etc/dphys-swapfile ]; then
      SWAPFILE=`cat /etc/dphys-swapfile | grep ^CONF_SWAPFILE | cut -f 2 -d=`
      if [ "$SWAPFILE" = "" ]; then
                SWAPFILE=/var/swap
      fi
      EXCLUDE_SWAPFILE="--exclude $SWAPFILE"
fi
sudo rsync --force -rltWDEgop --delete --stats --progress \
      $EXCLUDE_SWAPFILE \
      --exclude '.gvfs' \
      --exclude '/dev' \
      --exclude '/media' \
      --exclude '/mnt' \
      --exclude '/proc' \
      --exclude '/run' \
      --exclude '/sys' \
      --exclude '/tmp' \
      --exclude 'lost\+found' \
      --exclude '$usbmount' \
      // $mountr
# special dirs
for i in dev media mnt proc run sys boot; do
      if [ ! -d $mountr/$i ]; then
                sudo mkdir $mountr/$i
      fi
done
if [ ! -d $mountr/tmp ]; then
      sudo mkdir $mountr/tmp
      sudo chmod a+w $mountr/tmp
fi

#移除网络配置
#sudo rm -f $mountr/etc/udev/rules.d/70-persistent-net.rules

sync
ls -lia $mountr/home/pi/
echo "...Root partition done"
# if using the dump/restore
# tmp=$usbmount/root.ext4
# sudo chattr +d $img $mountb $mountr $tmp
# sudo mount -t ext4 ${device}p2 $mountr
# cd $mountr
# sudo dump -0uaf - / | sudo restore -rf -
# cd


# replace PARTUUID
opartuuidb=`blkid -o export /dev/mmcblk0p1 | grep PARTUUID`
opartuuidr=`blkid -o export /dev/mmcblk0p2 | grep PARTUUID`
npartuuidb=`blkid -o export ${device}p1 | grep PARTUUID`
npartuuidr=`blkid -o export ${device}p2 | grep PARTUUID`
sudo sed -i "s/$opartuuidr/$npartuuidr/g" $mountb/cmdline.txt
sudo sed -i "s/$opartuuidb/$npartuuidb/g" $mountr/etc/fstab
sudo sed -i "s/$opartuuidr/$npartuuidr/g" $mountr/etc/fstab

sudo umount $mountb
sudo umount $mountr

# umount loop device
sudo kpartx -d $loopdevice
sudo losetup -d $loopdevice
sudo umount $usbmount
rm -rf $mountb $mountr
echo "==== All done. You can un-plug the backup device"
```

保存成 `backup.sh`,然后赋予执行权限 `chmod +x backup.sh`

然后插上u盘,推荐找个空的u盘格式化成exfat格式,毕竟命令行很多人搞不懂磁盘工具。

执行 `./backup.sh`
这段只有一个参数输入,是否确定挂在/dev/sda1,这个就是u盘,挂在并创建镜像然后复制文件。

```bash
===================== part 1, install tools ===============================
正在读取软件包列表... 完成
正在分析软件包的依赖关系树
正在读取状态信息... 完成
dosfstools 已经是最新版 (4.1-2)。
exfat-fuse 已经是最新版 (1.3.0-1)。
kpartx 已经是最新版 (0.7.9-3)。
parted 已经是最新版 (3.2-25)。
pv 已经是最新版 (1.6.6-1)。
rsync 已经是最新版 (3.1.3-6)。
升级了 0 个软件包,新安装了 0 个软件包,要卸载 0 个软件包,有 3 个软件包未被升级。
===================== part 2, choose USB ===============================
no argument, assume the mount device is /dev/sda1 ? Y/N
y
FUSE exfat 1.3.0
WARN: volume was not unmounted cleanly.
备份文件位置:/mnt/rpi-back-20190822-2150.img
===================== part 3, create a new blank img ===============================
start create img,total size=2199256K
19.6MiB 0:00:11 [                         <=>
```

然后就等着复制就好了,如果是没有图形界面的树莓派系统,没有装什么大的应用,一般2.2个G左右。

出现这个就是制作完成了。


** 注意:如果需要配置新的网络只要还是配置上面那个网络文件放到boot下启动的时候会覆盖以前的wifi设置。**

** 注意:复制完成以后因为是通过缩小硬盘的方式缩小的,所以需要在启动以后通过 raspi-config--> 7高级设置--> A1 扩展文件系统来扩展 **



## 安装配置蓝牙网关

```shell
cd /opt/
git clone https://github.com/meishild/bt-mqtt-gateway.git
cd bt-mqtt-gateway
cp config.yaml.example config.yaml
```

config.yaml这个就是蓝牙网关的配置文件了。

我这个配置提供了三个我常用的花花草草、魅族、小米温度计、和蓝牙信号强度扫描的配置。

```yaml
mqtt:
host: 10.0.0.8
port: 1883
#username: user
#password: password
#ca_cert: /etc/ssl/certs/ca-certificates.crt # Uncomment to enable MQTT TLS, update path to appropriate location.
#ca_verify: False            # Verify TLS certificate chain and host, disable for testing with self-signed certificates, default to True
topic_prefix:                  # All messages will have that prefix added, remove if you dont need this.
client_id: bt-mqtt-gateway
availability_topic: lwt_topic

manager:
sensor_config:
    topic: homeassistant
    retain: true
topic_subscription:
    update_all:
      topic: homeassistant/status
      payload: online
command_timeout: 35         # Timeout for worker operations. Can be removed if the default of 35 seconds is sufficient.

workers:
    # 蓝牙强度
    btrssi:
      args:
      topic_prefix: rssi
      devices:
          name: 蓝牙的地址,不知道通过上面的那个测试获取
          name: 蓝牙的地址
      # 更新频次间隔,单位秒
      update_interval: 60
    # 花花草草
    miflora:
      args:
      devices:
          name: 蓝牙的地址
      topic_prefix: miflora
      update_interval: 300
    # 小米蓝牙温度计
    mithermometer:
      args:
      devices:
          name: 蓝牙的地址
      topic_prefix: mithermometer
      update_interval: 300
    # 魅族红外
    mzbtir:
      args:
      devices:
          name: 蓝牙的地址
      topic_prefix: mzbtir
      update_interval: 300
```

安装基础依赖:
```yaml
pip3 install -r requirements.txt
# 花花草草依赖
pip3 install miflora
# 小米依赖
pip3 install mithermometer
```

修改好了,可以通过直接启动测试以下 `python3 gateway.py`
这时候如果配置没有问题,就开始打印日志了.

```yaml
21:59:56 Starting
22时00分02秒 Adding 4 btrssi devices
22时00分04秒 Adding 2 miflora devices
22时00分07秒 Adding 1 mithermometer devices
22时00分07秒 Adding 1 mzbtir devices
22时00分12秒 Updating btrssi device 'xiezhualan' (C4:7C:8D:6B:0A:8C)
22时00分12秒 Updating btrssi device 'lvluo' (C4:7C:8D:6B:0B:E3)
22时00分12秒 Updating btrssi device 'mztest1' (68:3E:34:CC:DF:68)
22时00分12秒 Updating btrssi device 'mith_mr' (58:2D:34:34:A4:A7)
22时00分12秒 Updating 2 miflora devices
```

也可以直接通过mqtt消息查看发送了什么,需要新打开个控制台窗口执行: `mosquitto_sub -h 10.0.0.8 -t "#" -v`

```yaml
homeassistant/sensor/68-3E-34-CC-DF-68/btrssi_mztest1_rssi/config {"unique_id": "bt-mqtt-gateway/68-3E-34-CC-DF-68/btrssi_mztest1_rssi", "name": "btrssi_mztest1_rssi", "state_topic": "rssi/mztest1/rssi", "device": {"identifiers": ["68:3E:34:CC:DF:68", "bt-mqtt-gateway/68-3E-34-CC-DF-68/btrssi_mztest1"], "manufacturer": "bluetooth", "model": "rssi", "name": "btrssi_mztest1"}, "unit_of_measurement": "dBm"}
light_mr_closet/tele/LWT 在线
tele/sonoff/LWT 在线
tele/home_power/LWT 离线
localtest/lwt_topic online
mzbtir/test1/temperature 59.4
mzbtir/test1/humidity 28.94
mzbtir/test1/battery 20
rssi/xiezhualan/stat ON
rssi/xiezhualan/rssi_level 4
rssi/xiezhualan/rssi -81
rssi/lvluo/stat OFF
rssi/lvluo/rssi_level -1
rssi/mztest1/stat ON
rssi/mztest1/rssi_level 2
rssi/mztest1/rssi -92
rssi/mith_mr/stat ON
rssi/mith_mr/rssi_level 4
rssi/mith_mr/rssi -73
```

好了,整体配置完成了,但是这个是我们手动启动的下面切换成系统服务。

注意:这个bt-mqtt-gateway.service里面配置了目录和启动命令,大家根据自己情况修改,很多人不是放到了opt下面,那启动命令也一起改。

```yaml
cp bt-mqtt-gateway.service /etc/systemd/system/
# 开机自启动
systemctl enable bt-mqtt-gateway.service
# 启动服务,没有报错就是启动成功了
systemctl start bt-mqtt-gateway.service
# 查看服务状态
systemctl status bt-mqtt-gateway.service
```

```yaml
● bt-mqtt-gateway.service - Bluetooth MQTT gateway
   Loaded: loaded (/etc/systemd/system/bt-mqtt-gateway.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2019-08-22 22:02:52 CST; 32s ago
   Docs: https://github.com/zewelor/bt-mqtt-gateway
Main PID: 21941 (python3)
   Memory: 20.7M
   CGroup: /system.slice/bt-mqtt-gateway.service
         ├─21941 python3 /opt/bt-mqtt-gateway/gateway.py
         └─21949 /usr/local/lib/python3.7/dist-packages/bluepy/bluepy-helper 0

8月 22 22:03:11 raspberrypi gateway.py: 22:03:11 Starting
8月 22 22:03:15 raspberrypi gateway.py: 22:03:15 Adding 4 btrssi devices
8月 22 22:03:16 raspberrypi gateway.py: 22:03:16 Adding 2 miflora devices
8月 22 22:03:17 raspberrypi gateway.py: 22:03:17 Adding 1 mithermometer devices
8月 22 22:03:18 raspberrypi gateway.py: 22:03:18 Adding 1 mzbtir devices
8月 22 22:03:23 raspberrypi gateway.py: 22:03:23 Updating btrssi device 'xiezhualan' (C4:7C:8D:6B:0A:8C)
8月 22 22:03:23 raspberrypi gateway.py: 22:03:23 Updating btrssi device 'lvluo' (C4:7C:8D:6B:0B:E3)
8月 22 22:03:23 raspberrypi gateway.py: 22:03:23 Updating btrssi device 'mztest1' (68:3E:34:CC:DF:68)
8月 22 22:03:23 raspberrypi gateway.py: 22:03:23 Updating btrssi device 'mith_mr' (58:2D:34:34:A4:A7)
8月 22 22:03:23 raspberrypi gateway.py: 22:03:23 Updating 2 miflora devices
```

查看整个服务日志: `tail -100f /var/log/syslog`
因为这个是整个系统的日志,如果只看网关:``tail -100f /var/log/syslog | grep gateway``


## hass
网关也配置好了,如果hass开着mqtt自动发现应该就能在未使用设备列表或者状态列表看到了。





wyh260595711 发表于 2019-8-22 23:00:27

第一个报道,谢谢楼主大神,太感谢了

wqqs 发表于 2019-8-22 23:26:32

干货满满啊

xieahui 发表于 2019-8-23 08:10:23

额 这个N1可以套用吧……{:3_48:}

ghostist 发表于 2019-8-23 09:06:40

强烈要求出篇6~

junnikokuki 发表于 2019-8-23 10:00:53

70多还要加sd卡,感觉性价比不如esp32。

meishild 发表于 2019-8-23 10:32:01

xieahui 发表于 2019-8-23 08:10
额 这个N1可以套用吧……

可以啊,只要套用下面python插件安装以下的就可以了。

evantalk 发表于 2019-8-23 11:09:16

junnikokuki 发表于 2019-8-23 10:00
70多还要加sd卡,感觉性价比不如esp32。

确实esp32更合适

suifeng009 发表于 2019-8-23 12:02:46

太高产了。厉害了~

wyh260595711 发表于 2019-8-23 16:35:12

LZ又来求助了~~

页: [1] 2 3 4 5
查看完整版本: 篇七:树莓派zero w蓝牙网关