IOT应用之树莓派控制硬件开关

项目简介

IOT是当下相当火热的互联网趋势之一,笔者有幸参与到了一个IOT的项目当中,该项目是用于云端控制智能硬件的开关和视频录像监控,整个项目有三部分组成,云端服务器、智能硬件和树莓派。树莓派在这里充当的角色就是服务器和硬件的中间人,负责空间硬件的状态和数据的上传。

服务端和树莓派的通信

基于树莓派灵巧的架构和python的快速开发特点,该项目抛弃了之前的安卓应用,转为python应用,服务器和python之间的通信采用mqtt协议,认证方式为自制ca证书和密码。

树莓派和硬件的通信

树莓派和硬件直接的通信采用stm32交互,这里使用的是树莓派的AMA0串口。

Python应用的编写

服务器和硬件由别的团队负责,这里只简单介绍一下python端的代码编写过程和记录调试过程中的各种坑。

Python的版本选择

树莓派官方当前最新的系统版本是stretch,自带的python3版本是python3.5,而jessie的版本是python3.4,在mqtt通信还是串口通信的过程中貌似有bug,所以使用了3.5. 给jessie升级python的步骤有些繁琐,偷懒重新安装了系统。

MQTT通信

mqtt通信使用的python paho框架,需要注意的一点就是通信过程中使用了ca证书,这个在晚上的很多博客中没有具体的介绍,不同于用户密码那么简单。

1
2
3
4
...省略了很多的代码...
self._client.tls_set(cert_file, None,
None, cert_reqs=ssl.CERT_NONE, tls_version=ssl_tls, ciphers=None)
...又省略了很多的代码...

对于ca证书的使用,网上很多教程中只告诉了使用tls_set方法,填上证书的路径就完事了,但在实际使用过程中,不知道是不是自制证书的问题,cert_reqs=ssl.CERT_NONE 是必不可少的,否则会连接失败。

mqtt协议规定的最大上传数据是260M,但在实际测试过程中发现,大概2M以上的数据包服务器就会收不到,目前还不知道具体的问题出现在那个实现中,所以不推荐使用mqtt用来上传文件或者视频之类的业务。

STM32通信

串口通讯使用python的serial库,唯一需要注意的是连接串口的时候:

1
2
self._serial = serial.Serial(
port=port.device, baudrate=bps, timeout=timeout, bytesize=8, parity='N', stopbits=1)

port 的值是port.device 而不是port的name属性。如果是在windows平台,可能会不一样,跨平台写代码的时候需要注意这里的兼容性。

再一个就是读取串口数据的时候需要设置一个长度,根据你自己的需要灵活设置,一开始我设置的是60,后来某些数据包达到了2000多,所以后来又改成了4096.

1
2
3
4
5
def read(self, datalength=60):
'''
读取串口数据
'''
return self._serial.read(datalength)

二进制数据的CRC校验

串口通信为了保证数据的的一致性采用的验证方法是CRC校验,CRC校验又分很多中,最普遍的一种应该是Xmode,下面直接给出轮子:

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
57
class CRCCCITT(object):
crc_ccitt_tab = []
# The CRC's are computed using polynomials.
# Here is the most used coefficient for CRC CCITT
crc_ccitt_constant = 0x1021
def __init__(self, version='XModem'):
try:
dict_versions = {'XModem': 0x0000, 'FFFF': 0xffff, '1D0F': 0x1d0f}
if version not in dict_versions.keys():
raise Exception("Your version parameter should be one of \
the {} options".format("|".join(dict_versions.keys())))
self.starting_value = dict_versions[version]
# initialize the precalculated tables
if not len(self.crc_ccitt_tab):
self.init_crc_ccitt()
except Exception as e:
print("EXCEPTION(calculate): {}".format(e))
def calculate(self, input_data=None):
try:
is_string = isinstance(input_data, str)
is_bytes = isinstance(input_data, bytes)
if not is_string and not is_bytes:
raise Exception("Please provide a string or a byte sequence \
as argument for calculation.")
crcValue = self.starting_value
for c in input_data:
d = ord(c) if is_string else c
tmp = (c_ushort(crcValue >> 8).value) ^ d
crcValue = (c_ushort(crcValue << 8).value) ^ int(
self.crc_ccitt_tab[tmp], 0)
return crcValue
except Exception as e:
print("EXCEPTION(calculate): {}".format(e))
def init_crc_ccitt(self):
'''The algorithm uses tables with precalculated values'''
for i in range(0, 256):
crc = 0
c = i << 8
for j in range(0, 8):
if ((crc ^ c) & 0x8000):
crc = c_ushort(crc << 1).value ^ self.crc_ccitt_constant
else:
crc = c_ushort(crc << 1).value
c = c_ushort(c << 1).value # equivalent of c = c << 1
self.crc_ccitt_tab.append(hex(crc))

WIFI设置

wifi设置使用的是pywifi库,貌似目前只支持python3

录像

树莓派录像使用picamera库,但是录像的视频是h264格式,大多数播放器还不支持直接播放,推荐使用xmoplayer来播放或者转码为mp4格式。

调用浏览器

这个项目里给树莓派安排的一项重要任务就是播放广告,本来以为是视频,后来才被告知是一个URL链接,要用浏览器播放。

树莓派ssh直接调用UI应用是有问题的,会报

1
Error: Can't open display: :0.0

大概的意思就是ssh不能打开显示器,这里的解决方案是 export DISPLAY=:0

但是事情还没完,当前会话是可以调用浏览器了,但是项目使用systemd启动的,而且是root用户
由于树莓派默认登录的用户的是pi,root用户调用的命令默认是不让给pi显示的。

解决方案有两种,一种是在systemd中Serivce模块添加user=pi,但这个方案的问题在于pi的权限较小,如果程序中使用了一些/etc/、/tmp目录的话,通常会碰到权限问题。

第二种方案,就是在调用浏览器的代码中,切换到pi用户:

1
2
os.system(
"su -c 'chromium-browser --disable-popup-blocking --no-first-run --disable-desktop-notifications --kiosk {}' pi".format(url))

总结

模块的大体功能到此就基本结束了。还有一些小问题,比如串口粘包的问题,还没找到什么较好的解决方案。