步骤5
将行空板联网
1. 浏览器输入行空板默认地址10.1.2.3
2. 点击“网络设置”
3. 选择网络名称
4. 输入密码
5. 联网成功后会显示网络名称和IP地址
步骤6
代码编写
导入库
- from unihiker import GUI # 导入unihiker库
- import paho.mqtt.client as mqtt # 导入mqtt库
- import time # 导入time库
- from matplotlib.path import Path # Path库用于定义电子围栏
- import random
- import csv # 读取csv文件
- import requests # 对高德地图API进行地理编码和逆地理编码查询
- import pyttsx3 #离线语音合成
- from pinpong.board import Board # 从pinpong.board包中导入Board模块
复制代码
初始化板子和变量
- Board().begin() # 初始化,选择板型和端口号,不输入则进行自动识别
- gui = GUI() # 实例化GUI类,创建gui对象
- img = gui.draw_image(x=0,y=0,w=240, h=320, image='family.png') # 开机显示初始背景图为family
- pic_list = ['father.png', 'mother.png', 'grandfather.png', 'grandmother.png', 'daughter.png', 'son.png']
- engine = pyttsx3.init()
复制代码
设置MQTT参数
- #设置MQTT参数
- server = "182.254.130.180"
- port = 1883
- iot_id = "" #填写easyiot的用户id
- iot_pwd = "" #填写easyiot的密码
- topic = "" #填写easyiot的订阅主题
-
- # 以当前时间作为client_id
- client_id = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
- # ClientId不能重复,所以使用当前时间
- client = mqtt.Client(client_id)
- #设置连接的服务器、端口及keepalive
- client.connect(server,port , 30)
- # 设置连接用户和密码,必须设置,否则会返回Connected with result code 4
- client.username_pw_set(iot_id, iot_pwd)
复制代码
定义家庭成员电子围栏
读取已经编辑好的家庭成员电子围栏CSV文件,并转换为列表。
- #定义家庭成员电子围栏
- def family_fences():
- fences = [] # 存放所有家庭成员的电子围栏坐标
- with open('fence1.csv') as f:
- for row in csv.reader(f): # 每一行的数据是每个家庭成员的电子围栏经纬度,包括4组坐标
- tmp = [] #存放某个家庭成员的围栏坐标
- for data in row:
- # 每一行一共有4组数据,既围栏的4个点。每个点是一组(经度,纬度)值,以","分隔
- temp = data.split(',') # 将经纬度分割
- temp = [float(temp[0]),float(temp[1])] # 将经纬度由字符串转换为浮点数
- tmp.append(temp) # 将每个坐标点追加到该成员围栏列表
- fences.append(tmp) # 将完整的电子围栏坐标追加到成员列表中
- return fences # 返回所有家庭成员电子围栏
复制代码
显示成员相片及经纬度
当接收到成员位置更新后,显示该成员的相片和经纬度。
- #显示接收到的某个家庭成员对应的照片及当前位置,ID是家庭成员的序号,longi是经度,lati是纬度
- def show_pic(ID, longi, lati):
- img.config(image = 'photo/'+pic_list[int(ID)-1]) # 根据ID显示家庭成员图片
- gui.fill_rect(x=0, y=0, w=240, h=70, color=(247,246,249)) # 将前一次经纬度及地址信息覆盖
- gui.draw_text(x=5, y=0, text='经度:', font_size=10) # 显示“经度”
- gui.draw_text(x=50, y=0, text=longi, font_size=10) # 显示经度值
- gui.draw_text(x=5, y=15, text='纬度:', font_size=10) # 显示“纬度”
- gui.draw_text(x=50, y=15, text=lati, font_size=10) # 显示纬度值
复制代码
判断安全状态
根据返回的经纬度和ID判断该家庭成员是否在电子围栏范围内并使用语音播报结果。
- #显示当前家庭成员的位置是否在电子围栏范围内
- def show_status(ID, longi, lati):
- fence = Path(family_fences()[int(ID)-1]) # 获取当前家庭成员的电子围栏4个点的坐标
- status = fence.contains_point((float(longi), float(lati)))# 当前坐标是否在围栏范围内
- if status:
- #gui.draw_text(x=5, y=40, text='在电子围栏内', font_size=12, color='green') # 显示安全状态
- engine.say('在电子围栏内')
- else:
- #gui.draw_text(x=5, y=40, text='不在电子围栏内', font_size=12, color='red') # 显示可疑状态
- engine.say('不在电子围栏内')
- engine.runAndWait()
复制代码
逆地理编码查询
根据经纬度查询对应的地址和街道名。
-
- # 执行一次高德地图地理逆编码的查询
- def geocode(loca):
- url = 'https://restapi.amap.com/v3/geocode/regeo?key=a1e0c550b1068165e2274410cd7b8b9b&location=' #url前段
- url = url + loca
- response = requests.get(url=url) # 查询逆地理编码
- answer = response.json() # 返回获取的json形式的文本
- address = answer['regeocode']['formatted_address'] # 提取返回的格式化地址
- street = answer['regeocode']['addressComponent']['streetNumber']['street'] # 提取返回的街道信息
- print('规范地址:',address) # 地址
- print('街道:', street) # 街道名
- gui.draw_text(x=5, y=30, text=address[0:16], font_size=10) # 显示当前位置
- gui.draw_text(x=5, y=45, text=address[16:], font_size=10) # 返回的当前位置太长,分两行显示
复制代码
数据解析:
下图中的所有元素是变量answer返回的某个成员在某个位置时的一个值。
1. answer['regeocode']['formatted_address']的值是?
answer['regeocode']指整个返回值中键'regeocode'的值,是下图从{'addressComponent':开始到'formatted_address': '广东省深圳市南山区沙河街道香云路侨城一号广场'}结束,两个大括号之间的所有内容。
2. answer['regeocode']['addressComponent']['streetNumber']['street']的值是?
answer['regeocode']['formatted_address']的值就是'广东省深圳市南山区沙河街道香云路侨城一号广场'。
answer['regeocode']['addressComponent']的值是{'city': '深圳市',开始到'citycode': '0755'}结束,两个大括号之间的所有内容(青绿色开始,青绿色结束)。
answer['regeocode']['addressComponent']['streetNumber']的值是黄色的{'number': '340号', 开始到黄色的'street': '侨香路'}结束,两个大括号之间的所有内容。
answer['regeocode']['addressComponent']['streetNumber']['street']的值是'侨香路'。
如果要得到id的值'440305',变量要如何表示?
answer['regeocode']['addressComponent']['businessAreas'][0]['id']
下图是另一个地址的返回结果示例。可以看到返回的id解析的是正确的。
订阅发布MQTT消息
- # 连接mqtt并订阅消息
- def on_connect(client, userdata, flags, rc):
- client.subscribe(topic) # 填写订阅的主题
-
- #发布消息
- def on_publish(topic, payload, qos):
- client.publish(topic, payload, qos)
-
- #连接mqtt并接收消息
- def subscribe_msg():
- client.on_connect = on_connect
- client.on_message = on_message
- client.loop_forever()
复制代码
接收到消息后
调用函数显示家庭成员相片、经纬度、所在地址及安全状态。
- #当接收到订阅的消息后
- def on_message(client, userdata, msg):
- print(msg.topic+" " + ":" + str(msg.payload)) # 打印接收的消息
- message = str(msg.payload)[2:21] # 返回消息的格式为 b'1135159981101938488',从第3~21位是我们的数据,索引为2~20
- # print(message, message[0], message[1:10], message[10:19])
- familyID = message[0] # 第1位是家庭成员ID
- longitude = format(float(message[1:10]) / (10**6) - 100, '6f') #第2~10位是经度,转换为浮点数后除以10的6次方再减去100,保留小数点后7位,还原为经度的原始值
- latitude = format(float(message[10:19]) / (10**6) - 100, '.6f') #第11~19位是纬度,转换为浮点数后除以10的6次方再减去100,保留小数点后6位,还原为纬度的原始值
- # print(longitude, latitude)
- show_pic(familyID, longitude, latitude) # 调用函数在行空板显示图片及经纬度
- show_status(familyID, longitude, latitude) # 调用函数显示家庭成员状态
- location = str(longitude) +','+ str(latitude)
- geocode(location) # 调用函数查询逆地理编码(经纬度转换为规范地址)
复制代码
完整代码
- from unihiker import GUI # 导入unihiker库
- import paho.mqtt.client as mqtt # 导入mqtt库
- import time # 导入time库
- from matplotlib.path import Path # Path库用于定义电子围栏
- import random
- import csv # 读取csv文件
- import requests # 对高德地图API进行地理编码和逆地理编码查询
- import pyttsx3 #离线语音合成
- from pinpong.board import Board # 从pinpong.board包中导入Board模块
-
- Board().begin() # 初始化,选择板型和端口号,不输入则进行自动识别
- gui = GUI() # 实例化GUI类,创建gui对象
- img = gui.draw_image(x=0,y=0,w=240, h=320, image='family.png') # 开机显示初始背景图为family
- pic_list = ['father.png', 'mother.png', 'grandfather.png', 'grandmother.png', 'daughter.png', 'son.png']
- engine = pyttsx3.init()
-
- #设置MQTT参数
- server = "182.254.130.180"
- port = 1883
- iot_id = "" # 填写easyiot的用户id
- iot_pwd = "" # 填写easyiot的密码
- topic = "" # 填写easyiot的订阅主题
-
- # 以当前时间作为client_id
- client_id = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
- # ClientId不能重复,所以使用当前时间
- client = mqtt.Client(client_id)
- #设置连接的服务器、端口及keepalive
- client.connect(server,port , 30)
- # 设置连接用户和密码,必须设置,否则会返回Connected with result code 4
- client.username_pw_set(iot_id, iot_pwd)
-
- #定义家庭成员电子围栏
- def family_fences():
- fences = [] # 存放所有家庭成员的电子围栏坐标
- with open('fence1.csv') as f:
- for row in csv.reader(f): # 每一行的数据是每个家庭成员的电子围栏经纬度,包括4组坐标
- tmp = [] #存放某个家庭成员的围栏坐标
- for data in row:
- # 每一行一共有4组数据,既围栏的4个点。每个点是一组(经度,纬度)值,以","分隔
- temp = data.split(',') # 将经纬度分割
- temp = [float(temp[0]),float(temp[1])] # 将经纬度由字符串转换为浮点数
- tmp.append(temp) # 将每个坐标点追加到该成员围栏列表
- fences.append(tmp) # 将完整的电子围栏坐标追加到成员列表中
- return fences # 返回所有家庭成员电子围栏
-
- #显示接收到的某个家庭成员对应的照片及当前位置,ID是家庭成员的序号,longi是经度,lati是纬度
- def show_pic(ID, longi, lati):
- img.config(image = 'photo/'+pic_list[int(ID)-1]) # 根据ID显示家庭成员图片
- gui.fill_rect(x=0, y=0, w=240, h=70, color=(247,246,249)) # 将前一次经纬度及地址信息覆盖
- gui.draw_text(x=5, y=0, text='经度:', font_size=10) # 显示“经度”
- gui.draw_text(x=50, y=0, text=longi, font_size=10) # 显示经度值
- gui.draw_text(x=5, y=15, text='纬度:', font_size=10) # 显示“纬度”
- gui.draw_text(x=50, y=15, text=lati, font_size=10) # 显示纬度值
-
- #显示当前家庭成员的位置是否在电子围栏范围内
- def show_status(ID, longi, lati):
- fence = Path(family_fences()[int(ID)-1]) # 获取当前家庭成员的电子围栏4个点的坐标
- status = fence.contains_point((float(longi), float(lati)))
- if status:
- #gui.draw_text(x=5, y=40, text='在电子围栏内', font_size=12, color='green') # 显示安全状态
- engine.say('在电子围栏内')
- else:
- #gui.draw_text(x=5, y=40, text='不在电子围栏内', font_size=12, color='red') # 显示可疑状态
- engine.say('不在电子围栏内')
- engine.runAndWait()
-
- # 执行一次高德地图地理逆编码的查询
- def geocode(loca):
- url = 'https://restapi.amap.com/v3/geocode/regeo?key=a1e0c550b1068165e2274410cd7b8b9b&location=' #url前段
- url = url + loca
- response = requests.get(url=url) # 查询逆地理编码
- answer = response.json() # 返回获取的json形式的文本
- address = answer['regeocode']['formatted_address'] # 提取返回的格式化地址
- street = answer['regeocode']['addressComponent']['streetNumber']['street'] # 提取返回的街道信息
- print('规范地址:',address) # 地址
- print('街道:', street) # 街道名
- gui.draw_text(x=5, y=30, w=240, text=address, font_size=10) # 显示当前位置,文字超出长度自动换行
-
- # 连接mqtt并订阅消息
- def on_connect(client, userdata, flags, rc):
- client.subscribe(topic) # 填写订阅的主题
-
- #当接收到订阅的消息后
- def on_message(client, userdata, msg):
- print(msg.topic+" " + ":" + str(msg.payload)) # 打印接收的消息
- message = str(msg.payload)[2:21] # 返回消息的格式为 b'1135159981101938488',从第3~21位是我们的数据,索引为2~20
- # print(message, message[0], message[1:10], message[10:19])
- familyID = message[0] # 第1位是家庭成员ID
- longitude = format(float(message[1:10]) / (10**6) - 100, '6f') #第2~10位是经度,转换为浮点数后除以10的6次方再减去100,保留小数点后7位,还原为经度的原始值
- latitude = format(float(message[10:19]) / (10**6) - 100, '.6f') #第11~19位是纬度,转换为浮点数后除以10的6次方再减去100,保留小数点后6位,还原为纬度的原始值
- # print(longitude, latitude)
- show_pic(familyID, longitude, latitude) # 调用函数在行空板显示图片及经纬度
- show_status(familyID, longitude, latitude) # 调用函数显示家庭成员状态
- location = str(longitude) +','+ str(latitude)
- geocode(location) # 调用函数查询逆地理编码(经纬度转换为规范地址)
-
- #发布消息
- def on_publish(topic, payload, qos):
- client.publish(topic, payload, qos)
-
- #连接mqtt并接收消息
- def subscribe_msg():
- client.on_connect = on_connect
- client.on_message = on_message
- client.loop_forever()
-
- if __name__ == '__main__':
- subscribe_msg()
复制代码
演示视频
行空板通过远程桌面来观察其演示情况。
以下为视频链接
https://www.bilibili.com/video/BV1m94y1S7fV
https://www.bilibili.com/video/BV1n34y1n7Pj