步骤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