未验证 提交 5cae6e2c 编写于 作者: E ethanlcz 提交者: GitHub

haaseduk1 examples (#1799)

* modify HaaSPython's brief introduction, fix #1772
Signed-off-by: Nethan.lcz <ethan.lcz@alibaba-inc.com>

* add HaaS Python v2.2.0 release notest, fix #1726
Signed-off-by: Nethan.lcz <ethan.lcz@alibaba-inc.com>

* add README for HaaS EDU K1 examples, fix #1798
Signed-off-by: Nethan.lcz <ethan.lcz@alibaba-inc.com>
上级 9f958d24
{
"name": "haaseduk1",
"version": "1.0.0",
"io": {
"fire": {
"type": "ADC",
"port": 2,
"sampling": 12000000
},
"mpu6050": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 105
},
"ap3216c": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 30
},
"si7006": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 64
},
"spl06": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 119
},
"qmc5883": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 13
},
"cht8305": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 1000000,
"mode": "master",
"devAddr": 64
},
"qmi8610": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 106
},
"qmp6988": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 86
},
"qmc6310": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 28
},
"ADC2": {
"type": "ADC",
"port": 2,
"sampling": 12000000
},
"GPIO2": {
"type": "GPIO",
"port": 2,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"GPIO3": {
"type": "GPIO",
"port": 3,
"dir": "output",
"pull": "pullup"
},
"GPIO4": {
"type": "GPIO",
"port": 4,
"dir": "output",
"pull": "pullup"
},
"GPIO5": {
"type": "GPIO",
"port": 5,
"dir": "output",
"pull": "pullup"
},
"GPIO6": {
"type": "GPIO",
"port": 6,
"dir": "output",
"pull": "pullup"
},
"GPIO7": {
"type": "GPIO",
"port": 7,
"dir": "output",
"pull": "pullup"
},
"GPIO19": {
"type": "GPIO",
"port": 19,
"dir": "output",
"pull": "pullup"
},
"GPIO22": {
"type": "GPIO",
"port": 22,
"dir": "output",
"pull": "pullup"
},
"GPIO23": {
"type": "GPIO",
"port": 23,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"I2C1": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 60
},
"led_r": {
"type": "GPIO",
"port": 36,
"dir": "output",
"pull": "pullup"
},
"led_g": {
"type": "GPIO",
"port": 35,
"dir": "output",
"pull": "pullup"
},
"led_b": {
"type": "GPIO",
"port": 34,
"dir": "output",
"pull": "pullup"
},
"SPI0": {
"type": "SPI",
"port": 0,
"mode": "master",
"freq": 2000000
},
"oled_spi": {
"type": "SPI",
"port": 1,
"mode": "master",
"freq": 26000000
},
"oled_dc": {
"type": "GPIO",
"port": 28,
"dir": "output",
"pull": "pullup"
},
"oled_res": {
"type": "GPIO",
"port": 30,
"dir": "output",
"pull": "pullup"
},
"serial2": {
"type": "UART",
"port": 2,
"dataWidth": 8,
"baudRate": 9600,
"stopBits": 1,
"flowControl": "disable",
"parity": "none"
}
},
"debugLevel": "ERROR",
"repl": "disable"
}
\ No newline at end of file
from driver import ADC
class Fire(object):
def __init__(self, adcObj):
self.adcObj = None
if not isinstance(adcObj, ADC):
raise ValueError("parameter is not an ADC object")
self.adcObj = adcObj
def getVoltage(self):
if self.adcObj is None:
raise ValueError("invalid ADC object")
value = self.adcObj.readVoltage()
return value
# -*- encoding: utf-8 -*-
from aliyunIoT import Device # aliyunIoT组件是连接阿里云物联网平台的组件
import utime # 延时API所在组件
import ujson # json字串解析库
import fire
from driver import GPIO # HaaS EDU K1 LED使用GPIO进行控制
from driver import ADC # ADC类,通过微处理器的ADC模块读取ADC通道输入电压
import netmgr as nm # netmgr是Wi-Fi网络连接的组件
adcDev = 0 # ADC通道对象
ledDev = 0 # 报警LED对象
alarm_on = 0 # 记录报警状态
# 物联网平台连接标志位
iot_connected = False
# 三元组信息
productKey = "产品密钥"
deviceName = "设备名称"
deviceSecret = "设备密钥"
# Wi-Fi SSID和Password设置
wifiSsid = "请填写您的路由器名称"
wifiPassword = "请填写您的路由器密码"
# 物联网设备实例
device = None
def alarm_control(on):
global ledDev
if on == 0:
ledDev.write(0) # GPIO写入0,执行灭灯操作
else:
ledDev.write(1) # GPIO写入 1,执行亮灯报警动作
# 等待Wi-Fi成功连接到路由器
def get_wifi_status():
nm.init()
nm.disconnect()
wifi_connected = nm.getStatus()
# 连接到指定的路由器(路由器名称为wifiSsid, 密码为:wifiPassword)
print("start to connect " , wifiSsid)
nm.connect(wifiSsid, wifiPassword)
while True :
if wifi_connected == 5: # nm.getStatus()返回5代表连线成功
break
else:
wifi_connected = nm.getStatus() # 获取Wi-Fi连接路由器的状态信息
utime.sleep(0.5)
print("Wi-Fi connected")
print('DeviceIP:' + nm.getInfo()['ip']) # 打印Wi-Fi的IP地址信息
# 物联网平台连接成功的回调函数
def on_connect(data):
global iot_connected
iot_connected = True
# 设置props 事件接收函数(当云平台向设备下发属性时)
def on_props(request):
global alarm_on, device
# print(request)
payload = ujson.loads(request['params'])
# 获取dict状态字段 注意要验证键存在 否则会抛出异常
if "alarmState" in payload.keys():
alarm_on = payload["alarmState"]
if (alarm_on):
print("开始报警")
else:
print("结束报警")
# print(alarm_on)
# 根据云端设置的报警灯状态改变本地LED状态
alarm_control(alarm_on)
# 要将更改后的状态同步上报到云平台
prop = ujson.dumps({'alarmState': alarm_on})
# print('uploading data: ', prop)
upload_data = {'params': prop}
# 上报本地报警灯状态到云端
device.postProps(upload_data)
# 连接物联网平台
def connect_lk(productKey, deviceName, deviceSecret):
global device, iot_connected
key_info = {
'region': 'cn-shanghai',
'productKey': productKey,
'deviceName': deviceName,
'deviceSecret': deviceSecret,
'keepaliveSec': 60
}
# 将三元组信息设置到iot组件中
device = Device()
# 设定连接到物联网平台的回调函数,如果连接物联网平台成功,则调用on_connect函数
device.on(Device.ON_CONNECT, on_connect)
# 配置收到云端属性控制指令的回调函数
# 如果收到物联网平台发送的属性控制消息,则调用on_props函数
device.on(Device.ON_PROPS, on_props)
# 启动连接阿里云物联网平台过程
device.connect(key_info)
# 等待设备成功连接到物联网平台
while(True):
if iot_connected:
print('物联网平台连接成功')
break
else:
print('sleep for 1 s')
utime.sleep(1)
# 上传火焰传感器检测电压信息和报警信息到物联网平台
def upload_fire_detector_state():
global device, alarm_on
fireVoltage = 0
# 无限循环
while True:
fireVoltage = fireDev.getVoltage() # 获取电压值
# print("The fire status Voltage ",fireVoltage)
# 生成上报到物联网平台的属性值字串
# 此处的属性标识符"fireVoltage"和"alarmState"必须和物联网平台的属性一致
# "fireVoltage" - 代表燃气传感器测量到的电压值
# "alarmState" - 代表报警灯的当前状态
prop = ujson.dumps({
'fireVoltage': fireVoltage,
'alarmState': alarm_on
})
print("uploading data to the cloud, ", prop)
upload_data = {'params': prop}
# 上传火焰传感器测量结果和报警灯状态到物联网平台
device.postProps(upload_data)
# 每2秒钟上报一次
utime.sleep(2)
if __name__ == '__main__':
global fireDev
alarm_on = 0
# 硬件初始化
# 初始化 ADC
adcDev = ADC()
adcDev.open("fire")
fireDev = fire.Fire(adcDev)
# 初始化LED所连接GPIO
ledDev = GPIO()
ledDev.open("led_r")
alarm_control(alarm_on) # 关闭报警灯
# 请替换物联网平台申请到的产品和设备信息,可以参考文章:https://blog.csdn.net/HaaSTech/article/details/114360517
get_wifi_status()
connect_lk(productKey, deviceName, deviceSecret)
upload_fire_detector_state()
adcDev.close()
ledDev.close()
{
"name": "haaseduk1",
"version": "1.0.0",
"io": {
"mpu6050": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 105
},
"ap3216c": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 30
},
"si7006": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 64
},
"spl06": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 119
},
"qmc5883": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 13
},
"cht8305": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 1000000,
"mode": "master",
"devAddr": 64
},
"qmi8610": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 106
},
"qmp6988": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 86
},
"qmc6310": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 28
},
"ADC2": {
"type": "ADC",
"port": 2,
"sampling": 12000000
},
"GPIO2": {
"type": "GPIO",
"port": 2,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"GPIO3": {
"type": "GPIO",
"port": 3,
"dir": "output",
"pull": "pullup"
},
"GPIO4": {
"type": "GPIO",
"port": 4,
"dir": "output",
"pull": "pullup"
},
"GPIO5": {
"type": "GPIO",
"port": 5,
"dir": "output",
"pull": "pullup"
},
"GPIO6": {
"type": "GPIO",
"port": 6,
"dir": "output",
"pull": "pullup"
},
"GPIO7": {
"type": "GPIO",
"port": 7,
"dir": "output",
"pull": "pullup"
},
"GPIO19": {
"type": "GPIO",
"port": 19,
"dir": "output",
"pull": "pullup"
},
"GPIO22": {
"type": "GPIO",
"port": 22,
"dir": "output",
"pull": "pullup"
},
"GPIO23": {
"type": "GPIO",
"port": 23,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"I2C1": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 60
},
"led_r": {
"type": "GPIO",
"port": 36,
"dir": "output",
"pull": "pullup"
},
"led_g": {
"type": "GPIO",
"port": 35,
"dir": "output",
"pull": "pullup"
},
"led_b": {
"type": "GPIO",
"port": 34,
"dir": "output",
"pull": "pullup"
},
"SPI0": {
"type": "SPI",
"port": 0,
"mode": "master",
"freq": 2000000
},
"oled_spi": {
"type": "SPI",
"port": 1,
"mode": "master",
"freq": 26000000
},
"oled_dc": {
"type": "GPIO",
"port": 28,
"dir": "output",
"pull": "pullup"
},
"oled_res": {
"type": "GPIO",
"port": 30,
"dir": "output",
"pull": "pullup"
},
"serial2": {
"type": "UART",
"port": 2,
"dataWidth": 8,
"baudRate": 9600,
"stopBits": 1,
"flowControl": "disable",
"parity": "none"
}
},
"debugLevel": "ERROR",
"repl": "disable"
}
\ No newline at end of file
"""
Copyright (C) 2015-2021 Alibaba Group Holding Limited
MicroPython's driver for CHT8305
Author: HaaS
Date: 2021/09/14
"""
from micropython import const
from utime import sleep_ms
from driver import I2C
CHT8305_REG_TEMP = 0x00
CHT8305_REG_HUMI = 0x01
# The register address in CHT8305 controller.
class CHT8305Error(Exception):
def __init__(self, value=0, msg="cht8305 common error"):
self.value = value
self.msg = msg
def __str__(self):
return "Error code:%d, Error message: %s" % (self.value, str(self.msg))
__repr__ = __str__
class CHT8305(object):
"""
This class implements cht8305 chip's functions.
"""
def __init__(self, i2cDev):
self._i2cDev = None
if not isinstance(i2cDev, I2C):
raise ValueError("parameter is not an I2C object")
# make CHT8305's internal object points to i2cDev
self._i2cDev = i2cDev
def getTemperature(self):
"""Get temperature."""
# make sure CHT8305's internal object is valid before I2C operation
if self._i2cDev is None:
raise ValueError("invalid I2C object")
# send temperature register to CHT8305
reg = bytearray([CHT8305_REG_TEMP])
self._i2cDev.write(reg)
# wait for 30ms
sleep_ms(30)
readData = bytearray(2)
# read temperature from CHT8305
self._i2cDev.read(readData)
# convert the temperature data to actual value
value = (readData[0] << 8) | readData[1]
if (value & 0xFFFC):
temperature = (165.0 * float(value)) / 65536.0 - 40.0
else:
raise CHT8305Error("failed to get temperature.")
return temperature
def getHumidity(self):
"""Get humidity."""
# make sure CHT8305's internal object is valid before I2C operation
if self._i2cDev is None:
raise ValueError("invalid I2C object")
# send humidity register to CHT8305
reg = bytearray([CHT8305_REG_HUMI])
self._i2cDev.write(reg)
sleep_ms(30)
# read humidity from CHT8305
readData = bytearray(2)
self._i2cDev.read(readData)
# convert the humidity data to actual value
value = (readData[0] << 8) | readData[1]
if (value & 0xFFFE):
humidity = ((125.0 * float(value)) / 65535.0) - 6.0
else:
raise CHT8305Error("failed to get humidity.")
return humidity
def getTempHumidity(self):
"""Get temperature and humidity."""
# make sure CHT8305's internal object is valid before I2C operation
if self._i2cDev is None:
raise ValueError("invalid I2C object")
temphumidity = [0, 1]
# send temperature register to CHT8305
reg = bytearray([CHT8305_REG_TEMP])
self._i2cDev.write(reg)
sleep_ms(30)
# 4 bytes means read temperature and humidity back in one read operation
readData = bytearray(4)
self._i2cDev.read(readData)
#print("rawdata %d-%d-%d-%d" %(readData[0],readData[1],readData[2],readData[3]))
# convert the temperature and humidity data to actual value
value = (readData[0] << 8) | readData[1]
if (value & 0xFFFC):
temphumidity[0] = (165.0 * float(value)) / 65536.0 - 40.0
else:
raise CHT8305Error("failed to get temperature.")
value = (readData[2] << 8) | readData[3]
if (value & 0xFFFE):
temphumidity[1] = ((125.0 * float(value)) / 65535.0) - 6.0
else:
raise CHT8305Error("failed to get humidity.")
return temphumidity
if __name__ == "__main__":
'''
The below i2c configuration is needed in your board.json.
"cht8305": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 64
}
'''
print("Testing cht8305 ...")
i2cDev = I2C()
i2cDev.open("cht8305")
cht8305Dev = CHT8305(i2cDev)
temperature = cht8305Dev.getTemperature()
print("The temperature is: %f" % temperature)
humidity = cht8305Dev.getHumidity()
print("The humidity is: %f" % humidity)
i2cDev.close()
print("Test cht8305 done!")
"""
Copyright (C) 2015-2021 Alibaba Group Holding Limited
"""
from driver import I2C
import kv
class HAASEDUK1(object):
def __init__(self):
self.i2cDev = None
# 获取版本号
# 返回值为1,代表k1c
# 返回值为0,代表k1
def getHWID(self):
hwId = -1
result = kv.geti("HAASEDU_NAME")
if (result != None):
if (result == 1):
print("HAASEDUK1 hw version is 1.1")
return 1
elif (result == 0):
print("HAASEDUK1 hw version is 1.0")
return 0
else:
pass
# kv中不存在HAASEDU_NAME键值对,则通过外围传感器进行判断
# 读取QMI8610传感器device identifier register的值
self.i2cDev = I2C()
self.i2cDev.open("qmi8610")
buf = bytearray(1)
buf[0] = 0
self.i2cDev.memRead(buf, 0, 8) # register address:0 - FIS device identifier register address
self.i2cDev.close()
if buf[0] == 0xfc:
hwId = 1
else:
# 读取QMI8610传感器chip id register的值
self.i2cDev.open("qmp6988")
buf[0] = 0xD1 # chip id register
self.i2cDev.write(buf)
self.i2cDev.read(buf)
self.i2cDev.close()
if buf[0] == 0x5C:
hwId = 1
else:
# 读取QMC6310传感器chip id register的值
self.i2cDev.open("qmc6310")
buf[0] = 0x00 # chip id register
self.i2cDev.write(buf)
self.i2cDev.read(buf)
self.i2cDev.close()
if buf[0] == 0x80:
hwId = 1
if hwId == 1:
kv.seti("HAASEDU_NAME", 1)
print("HAASEDUK1 hw version is 1.1")
return 1
else:
kv.seti("HAASEDU_NAME", 0)
print("HAASEDUK1 hw version is 1.0")
return 0
# -*- encoding: utf-8 -*-
'''
@File : main.py
@Description: 温湿度上云
@Author : ethan.lcz
@version : 1.0
'''
from aliyunIoT import Device # aliyunIoT组件是连接阿里云物联网平台的组件
import netmgr as nm # Wi-Fi功能所在库
import utime # 延时API所在组件
from driver import I2C # I2C总线驱动库
from driver import GPIO # ESP32和使用GPIO控制LED
from haaseduk1 import HAASEDUK1 # 引入haaseduk1库,目标用于区分K1版本
import ujson # json字串解析库
# 物联网平台连接标志位
iot_connected = False
# 物联网设备实例
device = None
# 三元组信息
productKey = "产品密钥"
deviceName = "设备名称"
deviceSecret = "设备密钥"
# Wi-Fi SSID和Password设置
wifiSsid = "请填写您的路由器名称"
wifiPassword = "请填写您的路由器密码"
# 空调和加湿器状态变量
airconditioner = 0
humidifier = 0
airconditioner_value = 0
humidifier_value = 0
humitureDev = 0
board = HAASEDUK1() # 新建HAASEDUK1对象
hwID = board.getHWID() # 获取开发板ID
i2cObj = I2C()
if (hwID == 1):
from cht8305 import CHT8305 # HaaS EDU K1C上的温湿度传感器采用的是CHT8305
i2cObj.open("cht8305") # 按照board.json中名为"cht8305"的设备节点的配置参数(主设备I2C端口号,从设备地址,总线频率等)初始化I2C类型设备对象
humitureDev = CHT8305(i2cObj) # 初始化CHT8305传感器
# print("cht8305 inited!")
else:
from si7006 import SI7006 # HaaS EDU K1上的温湿度传感器采用的是SI7006
i2cObj.open("si7006") # 按照board.json中名为"si7006"的设备节点的配置参数(主设备I2C端口号,从设备地址,总线频率等)初始化I2C类型设备对象
humitureDev = SI7006(i2cObj) # 初始化SI7006传感器
version = humitureDev.getVer() # 获取SI7006的版本信息
chipID = humitureDev.getID() # 获取SI7006 ID信息
# print("si7006 version:%d, chipID:%d" , version, chipID)
# 等待Wi-Fi成功连接到路由器
def get_wifi_status():
nm.init()
nm.disconnect()
wifi_connected = nm.getStatus()
print("start to connect " , wifiSsid)
nm.connect(wifiSsid, wifiPassword) # 连接到指定的路由器(路由器名称为wifiSsid, 密码为:wifiPassword)
while True :
if wifi_connected == 5: # nm.getStatus()返回5代表连线成功
break
else:
wifi_connected = nm.getStatus() # 获取Wi-Fi连接路由器的状态信息
utime.sleep(0.5)
print("Wi-Fi connected")
print('DeviceIP:' + nm.getInfo()['ip']) # 打印Wi-Fi的IP地址信息
# 通过温湿度传感器读取温湿度信息
def get_temp_humi():
global humitureDev
'''
# 如果需要同时获取温湿度信息,可以呼叫getTempHumidity,实例代码如下:
humniture = humitureDev.getTempHumidity() # 获取温湿度传感器测量到的温湿度值
temperature = humniture[0] # get_temp_humidity返回的字典中的第一个值为温度值
humidity = humniture[1] # get_temp_humidity返回的字典中的第二个值为相对湿度值
'''
temperature = humitureDev.getTemperature() # 获取温度测量结果
# print("The temperature is: %.1f" % temperature)
humidity = humitureDev.getHumidity() # 获取相对湿度测量结果
# print("The humidity is: %d" % humidity)
return temperature, humidity # 返回读取到的温度值和相对湿度值
# 物联网平台连接成功的回调函数
def on_connect(data):
global iot_connected
iot_connected = True
# 设置props 事件接收函数(当云平台向设备下发属性时)
def on_props(request):
global airconditioner, humidifier, airconditioner_value, humidifier_value
# {"airconditioner":1} or {"humidifier":1} or {"airconditioner":1, "humidifier":1}
payload = ujson.loads(request['params'])
# print (payload)
# 获取dict状态字段 注意要验证键存在 否则会抛出异常
if "airconditioner" in payload.keys():
airconditioner_value = payload["airconditioner"]
if (airconditioner_value):
print("打开空调")
else:
print("关闭空调")
if "humidifier" in payload.keys():
humidifier_value = payload["humidifier"]
if (humidifier_value):
print("打开加湿器")
else:
print("关闭加湿器")
# print(airconditioner_value, humidifier_value)
airconditioner.write(airconditioner_value) # 控制空调开关
humidifier.write(humidifier_value) # 控制加湿器开关
# 要将更改后的状态同步上报到云平台
prop = ujson.dumps({
'airconditioner': airconditioner_value,
'humidifier': humidifier_value,
})
upload_data = {'params': prop}
# 上报空调和加湿器属性到云端
device.postProps(upload_data)
# 连接物联网平台
def connect_lk(productKey, deviceName, deviceSecret):
global device, iot_connected
key_info = {
'region': 'cn-shanghai',
'productKey': productKey,
'deviceName': deviceName,
'deviceSecret': deviceSecret,
'keepaliveSec': 60
}
# 将三元组信息设置到iot组件中
device = Device()
# 设定连接到物联网平台的回调函数,如果连接物联网平台成功,则调用on_connect函数
device.on(Device.ON_CONNECT, on_connect)
# 配置收到云端属性控制指令的回调函数,如果收到物联网平台发送的属性控制消息,则调用on_props函数
device.on(Device.ON_PROPS, on_props)
# 启动连接阿里云物联网平台过程
device.connect(key_info)
# 等待设备成功连接到物联网平台
while(True):
if iot_connected:
print('物联网平台连接成功')
break
else:
print('sleep for 1 s')
utime.sleep(1)
# 上传温度信息和湿度信息到物联网平台
def upload_temperature_and_Humidity():
global device
while True:
data = get_temp_humi() # 读取温度信息和湿度信息
# 生成上报到物联网平台的属性值字串
prop = ujson.dumps({
'CurrentTemperature': data[0],
'CurrentHumidity': data[1]
})
print('uploading data: ', prop)
upload_data = {'params': prop}
# 上传温度和湿度信息到物联网平台
device.postProps(upload_data)
utime.sleep(2)
if __name__ == '__main__':
# 硬件初始化
# 初始化 GPIO
airconditioner = GPIO()
humidifier = GPIO()
humidifier.open('led_g') # 加湿器使用board.json中led_g节点定义的GPIO,对应HaaS EDU K1上的绿灯
airconditioner.open('led_b') # 空调使用board.json中led_b节点定义的GPIO,对应HaaS EDU K1上的蓝灯
# 请替换物联网平台申请到的产品和设备信息,可以参考文章:https://blog.csdn.net/HaaSTech/article/details/114360517
# global productKey, deviceName, deviceSecret ,on_request, on_play
get_wifi_status()
connect_lk(productKey, deviceName, deviceSecret)
upload_temperature_and_Humidity()
humitureDev.close()
"""
Copyright (C) 2015-2021 Alibaba Group Holding Limited
MicroPython's drive for SI7006
Author: HaaS
Date: 2021/09/09
"""
from driver import I2C
from utime import sleep_ms
# The commands provided by SI7006
Si7006_MEAS_REL_HUMIDITY_MASTER_MODE = 0xE5
Si7006_MEAS_REL_HUMIDITY_NO_MASTER_MODE = 0xF5
Si7006_MEAS_TEMP_MASTER_MODE = 0xE3
Si7006_MEAS_TEMP_NO_MASTER_MODE = 0xF3
Si7006_READ_OLD_TEMP = 0xE0
Si7006_RESET = 0xFE
Si7006_READ_ID_LOW_0 = 0xFA
Si7006_READ_ID_LOW_1 = 0x0F
Si7006_READ_ID_HIGH_0 = 0xFC
Si7006_READ_ID_HIGH_1 = 0xC9
Si7006_READ_Firmware_Revision_0 = 0x84
Si7006_READ_Firmware_Revision_1 = 0xB8
class SI7006Error(Exception):
def __init__(self, value=0, msg="si7006 common error"):
self.value = value
self.msg = msg
def __str__(self):
return "Error code:%d, Error message: %s" % (self.value, str(self.msg))
__repr__ = __str__
class SI7006(object):
"""
This class implements SI7006 chip's functions.
"""
# i2cDev should be an I2C object and it should be opened before __init__ is called
def __init__(self, i2cDev):
self._i2cDev = None
if not isinstance(i2cDev, I2C):
raise ValueError("parameter is not an I2C object")
# make SI7006's internal object points to i2cDev
self._i2cDev = i2cDev
def getVer(self):
"""
Get the firmware version of the chip.
"""
# make sure SI7006's internal object is valid before I2C operation
if self._i2cDev is None:
raise ValueError("invalid I2C object")
# send read firmware version command to SI7006
reg = bytearray([Si7006_READ_Firmware_Revision_0, Si7006_READ_Firmware_Revision_1])
self._i2cDev.write(reg)
sleep_ms(30)
version = bytearray(1)
# read the version info back
self._i2cDev.read(version)
return version[0]
def getID(self):
"""Get the chip ID."""
# make sure SI7006's internal object is valid before I2C operation
if self._i2cDev is None:
raise ValueError("invalid I2C object")
# send read chip id‘s lower part command to SI7006
reg = bytearray([Si7006_READ_ID_LOW_0, Si7006_READ_ID_LOW_1])
self._i2cDev.write(reg)
sleep_ms(30)
id_buf_low = bytearray(4)
# read the id info back
self._i2cDev.read(id_buf_low)
# send read chip id‘s higher part command to SI7006
reg = bytearray([Si7006_READ_ID_HIGH_0, Si7006_READ_ID_HIGH_1])
id_buf_high = bytearray(4)
self._i2cDev.read(id_buf_high)
return id_buf_low + id_buf_high
def getTemperature(self):
"""Get temperature."""
# make sure SI7006's internal object is valid before I2C operation
if self._i2cDev is None:
raise ValueError("invalid I2C object")
# send measure temperature command to SI7006
reg = bytearray([Si7006_MEAS_TEMP_NO_MASTER_MODE])
self._i2cDev.write(reg)
# wait for 30ms to wait for the measure finish according to SI7006's datasheet
sleep_ms(30)
readData = bytearray(2)
# read the temperature measure result
self._i2cDev.read(readData)
value = (readData[0] << 8 | readData[1])
# convert to actual temperature
if (value & 0xFFFC):
temperature = (175.72 * value) / 65536.0 - 46.85
return round(temperature, 1)
else:
raise SI7006Error("failed to get temperature.")
def getHumidity(self):
"""Get humidity."""
# make sure SI7006's internal object is valid before I2C operation
if self._i2cDev is None:
raise ValueError("invalid I2C object")
# send measure humidity command to SI7006
reg = bytearray([Si7006_MEAS_REL_HUMIDITY_NO_MASTER_MODE])
self._i2cDev.write(reg)
# wait for 30ms to wait for the measure finish according to SI7006's datasheet
sleep_ms(30)
readData = bytearray(2)
self._i2cDev.read(readData)
value = (readData[0] << 8) | readData[1]
# convert to actual humidity
if (value & 0xFFFE):
humidity = (125.0 * value) / 65535.0 - 6.0
return round(humidity)
else:
raise SI7006Error("failed to get humidity.")
def getTempHumidity(self):
"""Get temperature and humidity."""
# make sure SI7006's internal object is valid before I2C operation
if self._i2cDev is None:
raise ValueError("invalid I2C object")
# read tempperature and humidity in sequence
temphumidity = [0, 0]
temphumidity[0] = self.getTemperature()
temphumidity[1] = self.getHumidity()
return temphumidity
if __name__ == "__main__":
'''
The below i2c configuration is needed in your board.json.
"si7006": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 64
}
'''
print("Testing si7006 ...")
i2cDev = I2C()
i2cDev.open("si7006")
si7006Dev = SI7006(i2cDev)
version = si7006Dev.getVer()
print("si7006 version is: %d" % version)
chipID = si7006Dev.getID()
print("si7006 chip id is:", chipID)
temperature = si7006Dev.getTemperature()
print("The temperature is: %f" % temperature)
humidity = si7006Dev.getHumidity()
print("The humidity is: %d" % humidity)
i2cDev.close()
print("Test si7006 done!")
{
"name": "haasedu",
"version": "1.0.0",
"io": {
"mq2": {
"type": "ADC",
"port": 2,
"sampling": 12000000
},
"led": {
"type": "GPIO",
"port": 36,
"dir": "output",
"pull": "pullup"
},
"led_r": {
"type": "GPIO",
"port": 36,
"dir": "output",
"pull": "pullup"
},
"ap3216c": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 30
},
"mpu6050": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 105
},
"si7006": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 64
},
"spl06": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 119
},
"qmc5883": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 13
},
"GPIO2": {
"type": "GPIO",
"port": 2,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"GPIO3": {
"type": "GPIO",
"port": 3,
"dir": "output",
"pull": "pullup"
},
"GPIO4": {
"type": "GPIO",
"port": 4,
"dir": "output",
"pull": "pullup"
},
"GPIO5": {
"type": "GPIO",
"port": 5,
"dir": "output",
"pull": "pullup"
},
"GPIO6": {
"type": "GPIO",
"port": 6,
"dir": "output",
"pull": "pullup"
},
"GPIO7": {
"type": "GPIO",
"port": 7,
"dir": "output",
"pull": "pullup"
},
"GPIO19": {
"type": "GPIO",
"port": 19,
"dir": "output",
"pull": "pullup"
},
"GPIO22": {
"type": "GPIO",
"port": 22,
"dir": "output",
"pull": "pullup"
},
"KEY_1": {
"type": "GPIO",
"port": 23,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"KEY_2": {
"type": "GPIO",
"port": 20,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"KEY_3": {
"type": "GPIO",
"port": 21,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"KEY_4": {
"type": "GPIO",
"port": 26,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"I2C1": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 60
},
"led_g": {
"type": "GPIO",
"port": 35,
"dir": "output",
"pull": "pullup"
},
"led_b": {
"type": "GPIO",
"port": 34,
"dir": "output",
"pull": "pullup"
},
"SPI0": {
"type": "SPI",
"port": 0,
"mode": "master",
"freq": 2000000
},
"oled_spi": {
"type": "SPI",
"port": 1,
"mode": "master",
"freq": 26000000
},
"oled_dc": {
"type": "GPIO",
"port": 28,
"dir": "output",
"pull": "pullup"
},
"oled_res": {
"type": "GPIO",
"port": 30,
"dir": "output",
"pull": "pullup"
},
"serial2": {
"type": "UART",
"port": 2,
"dataWidth": 8,
"baudRate": 115200,
"stopBits": 1,
"flowControl": "disable",
"parity": "none"
}
},
"debugLevel": "ERROR",
"repl": "disable"
}
\ No newline at end of file
# -*- encoding: utf-8 -*-
'''
@File : main.py
@Description: 燃气检测和报警灯控制
@Author : ethan.lcz
@version : 1.0
'''
from aliyunIoT import Device # aliyunIoT组件是连接阿里云物联网平台的组件
import utime # 延时API所在组件
import ujson # json字串解析库
from driver import GPIO # 开发板 LED使用GPIO进行控制
from driver import ADC # ADC类,通过微处理器的ADC模块读取ADC通道输入电压
import netmgr as nm # netmgr是Wi-Fi网络连接的组件
import mq2 # 引入MQ2传感器驱动库
import ujson # json库
adcObj = 0 # ADC类型的外设对象
mq2Dev = 0 # MQ2燃气传感器对象
alarmLed = 0 # 报警LED对象
alarmOn = 0 # 记录报警状态
# 物联网平台连接标志位
iot_connected = False
# Wi-Fi SSID和Password设置
wifiSsid = "请填写您的路由器名称"
wifiPassword = "请填写您的路由器密码"
# 三元组信息
productKey = "产品密钥"
deviceName = "设备名称"
deviceSecret = "设备密钥"
wlan = None
# 物联网设备实例
device = None
# 等待Wi-Fi成功连接到路由器
def get_wifi_status():
nm.init()
nm.disconnect()
wifi_connected = nm.getStatus()
print("start to connect " + wifiSsid)
nm.connect(wifiSsid, wifiPassword) # 连接到指定的路由器(路由器名称为wifiSsid, 密码为:wifiPassword)
while True :
if wifi_connected == 5: # nm.getStatus()返回5代表连线成功
break
else:
wifi_connected = nm.getStatus() # 获取Wi-Fi连接路由器的状态信息
utime.sleep(0.5)
print("wifi_connected: " + wifi_connected)
print("Wi-Fi connected")
print('DeviceIP:' + nm.getInfo()['ip']) # 打印Wi-Fi的IP地址信息
# 设置props 事件接收函数(当云平台向设备下发属性时)
def on_props(request):
global alarmOn
# print(request)
# {"alarmLight":1} or {"alarmLight":0}
payload = ujson.loads(request['params'])
# 获取dict状态字段 注意要验证键存在 否则会抛出异常
if "alarmLight" in payload.keys():
print(payload)
alarmOn = payload["alarmLight"]
if (alarmOn):
print("开始报警")
else:
print("结束报警")
# 根据云端设置的报警灯状态改变本地LED状态
led_control(alarmOn)
# 要将更改后的状态同步上报到云平台
data = ujson.dumps({'alarmLight': alarmOn,})
uploadData = { 'params': data }
# 上报本地报警灯状态到云端
device.postProps(uploadData)
# 物联网平台连接成功的回调函数
def on_connect(data):
global iot_connected
iot_connected = True
def connect_lk(productKey, deviceName, deviceSecret):
global device, iot_connected
key_info = {
'region': 'cn-shanghai',
'productKey': productKey,
'deviceName': deviceName,
'deviceSecret': deviceSecret,
'keepaliveSec': 60
}
# 将三元组信息设置到iot组件中
device = Device()
# 设定连接到物联网平台的回调函数,如果连接物联网平台成功,则调用on_connect函数
device.on(Device.ON_CONNECT, on_connect)
# 配置收到云端属性控制指令的回调函数,如果收到物联网平台发送的属性控制消息,则调用on_props函数
device.on(Device.ON_PROPS, on_props)
# 启动连接阿里云物联网平台过程
device.connect(key_info)
# 等待设备成功连接到物联网平台
while(True):
if iot_connected:
print('物联网平台连接成功')
break
else:
print('sleep for 1 s')
utime.sleep(1)
def led_control(on):
global alarmLed
if on == 0:
alarmLed.write(0) # GPIO写入0,执行灭灯操作
else:
alarmLed.write(1) # GPIO写入 1,执行亮灯报警动作
# 上传燃气传感器测量结果和报警灯状态到物联网平台
def upload_gas_detector_state():
global device, alarmOn, mq2Dev
gasVoltage = 0
# 无限循环
while True:
gasVoltage = mq2Dev.getVoltage() # 呼叫mq2传感器库提供的getVoltage函数获取电压值,单位:mV
data = ujson.dumps({
'gasVoltage': gasVoltage,
'alarmLight': alarmOn
})
# 生成上报到物联网平台的属性值字串,此处的属性标识符"gasVoltage"和"alarmLight"必须和物联网平台的属性一致
# "gasVoltage" - 代表燃气传感器测量到的电压值
# "alarmLight" - 代表报警灯的当前状态
print('reporting data to cloud:', data)
uploadData = {'params': data}
# 上传燃气传感器测量结果和报警灯状态到物联网平台
device.postProps(uploadData)
# 每2秒钟上报一次
utime.sleep(2)
if __name__ == '__main__':
# 硬件初始化
# 初始化 ADC
adcObj = ADC() # ADC通道对象
adcObj.open("mq2") # 按照board.json中名为"mq2"节点的配置初始化ADC设备对象
mq2Dev = mq2.MQ2(adcObj) # 初始化MQ2设备对象
# 初始化红色LED所连接GPIO
alarmLed = GPIO()
alarmLed.open('led') # LED报警灯使用board.json中名为led的节点中定义的GPIO进行控制
led_control(alarmOn) # 关闭报警灯
# 请替换物联网平台申请到的产品和设备信息,可以参考文章:https://blog.csdn.net/HaaSTech/article/details/114360517
# global productKey, deviceName, deviceSecret ,on_request, on_play
get_wifi_status()
connect_lk(productKey, deviceName, deviceSecret)
upload_gas_detector_state()
adcObj.close()
alarmLed.close()
from driver import ADC
class MQ2(object):
def __init__(self, adcObj):
self.adcObj = None
if not isinstance(adcObj, ADC):
raise ValueError("parameter is not an ADC object")
self.adcObj = adcObj
def getVoltage(self):
if self.adcObj is None:
raise ValueError("invalid ADC object")
value = self.adcObj.readVoltage()
return value
# 甲醛浓度检测系统
&emsp;&emsp;
下图是本案例除硬件连线外的2步导学,每个步骤中实现的功能请参考图中的说明。在硬件连线完成之后我们建议您先使用“一分钟上云体验”功能预先体验本案例的实际运行效果。
<div align="center">
<img src=./../../../images/2_甲醛检测系统_步骤概述.jpg width=70%/>
</div>
## 1、简介
&emsp;&emsp;
甲醛(化学式HCHO或CH2O)是一种有特殊刺激气味的气体,对人的眼睛和鼻子有强烈的刺激作用。若空气中甲醛浓度过高,比如新装修的房子、新买的汽车等,可引起中毒反应,严重的可致癌。甲醛是空气中的杀手之一,需要时刻提防。
### 1.1、背景知识
&emsp;&emsp;
本系统的核心在于如何精准的检测家庭中的甲醛浓度。现在市面上有很多种甲醛检测的仪器,大多企业在用的都是通过电化学的方法来检测。
<div align="center">
<img src=./../../../images/2_电化学甲醛传感器.jpeg width=30%/>
</div>
&emsp;&emsp;
电化学甲醛传感器对比其它方式来检测的的抗干扰能力强,响应专一,灵敏度高,测量结果精确,检测下限低,恢复-响应特性佳。
&emsp;&emsp;
本节选用的是HCHO甲醛传感器,可精确测量空气中的甲醛浓度,并能抑制干扰气体,具有稳定性高、抗干扰气体能力强等特点。分辨率高达0.01ppm,支持3.3~6V宽电压输入,具备良好的兼容性,并且使用寿命长达2年。 简单易用的Gravity接口、宽输入电压、支持模拟电压或者串口输出,几乎可兼容所有的主控器。
<div align="center">
<img src=./../../../images/2_DFROBOT甲醛传感器.jpeg width=30%/>
</div>
> 甲醛传感器在使用之前需要预热5分钟以上。
### 1.2、准备
* [HaaS EDU K1开发板](https://haas.iot.aliyun.com/solution/detail/hardware?versionId=800C5AB3B8A4A88800000001&dataId=800C5AB3B8A4A888) 一台
* [HCHO甲醛传感器](https://haas.iot.aliyun.com/solution/detail/hardware?versionId=800C4F2BE3C4C4E800000002&dataId=800C4F2BE3C4C4E8) 一个
* 杜邦连接线若干
&emsp;&emsp;
硬件连线图如下图所示:
<div align="center">
<img src=./../../../images/2_甲醛检测_HaaS_EDU_K1_HCHO_DAC_连线.jpg width=90%/>
</div>
<br>
## 3、物联网平台开发
### 3.1、开通公共实例
&emsp;&emsp;
对于第一次使用物联网平台的读者,需要开通实例以使用物联网平台的功能。这里可以使用免费的公共实例进行开发。
&emsp;&emsp;
[物联网平台](https://iot.console.aliyun.com/lk/summary/new)中,左上角选择“华东2-上海”,点击“公共实例”,即可开通。
<div align="center">
<img src=./../../../images/5_3_开通公共实例.png
width=100%/>
</div>
&emsp;&emsp;
开通物联网平台功能之后,需要完成下面的3个步骤完成云端设备的创建:
1. 创建云端产品
2. 创建产品属性(物模型)
3. 创建云端设备(获取三元组)
### 3.2、创建云端产品
&emsp;&emsp;
点击上图中的“公共实例”,即可进入[控制台](https://iot.console.aliyun.com/lk/summary/new)进行产品创建。然后,点击创建产品按钮,如下图所示。
<div align="center">
<img src=./../../../images/1_创建产品.png
width=100%/>
</div>
&emsp;&emsp;
在新建产品设定页面按照下图所示,设定“产品名称”,选择所属的“自定义品类”(自定义品类的物模型为空,需要自己创建,也可以通过导入外部物模型的方式导入),节点类型选择“直连设备”,联网方式选择“Wi-Fi”,数据格式选择“ICA标准数据格式”,检验类型和认证方式选择默认设定即可。还可以根据开发者自己的需求在“产品描述”页面添加针对此产品的描述。
<div align="center">
<img src=./../../../images/2_新建甲醛检测设备.png width=50%/>
</div>
&emsp;&emsp;
选择之后,点击“确认”按钮,即可完成产品创建。返回“产品”页面之后可以看到产品类表中会出现刚刚创建的“甲醛检测”的产品,如下图所示。
<div align="center">
<img src=./../../../images/2_甲醛检测系统_产品列表页.png width=100%/>
</div>
<br>
### 3.3、创建产品属性(物模型)
&emsp;&emsp;
点击上图中的“查看”按钮,即可看到产品信息,Topic列表,功能定义,数据解析等跟产品相关功能的设定。点开“功能定义”标签页,可以看到设备物模型定义。
<div align="center">
<img src=./../../../images/2_甲醛检测系统_产品详情页面.png width=100%/>
</div>
&emsp;&emsp;
标识符是设备端上报设备属性状态的消息中需要使用的标识符,并且只有在设备上报的属性内容符合“数据定义”中的数据取值范围的时候才会被物联网平台记录,否则会被物联网平台认定为非法属性而过滤掉。
&emsp;&emsp;
本节我们选择创建自定义物模型的方式来创建此系统需要的物模型信息,点击上图中的”编辑草稿“按钮。然后按照下图的步骤,选择添加自定义功能。
<div align="center">
<img src=./../../../images/2_甲醛检测_创建自定义物模型1.png width=100%/>
</div>
然后按照下图选择甲醛浓度属性添加。
<div align="center">
<img src=./../../../images/2_甲醛检测_创建自定义物模型2.png width=50%/>
</div>
&emsp;&emsp;
物模型添加成功之后可以看到网页出现了我们刚刚创建的物模型属性。其中HCHO代表甲醛传感器检测到的浓度值,数据类型为double浮点型,单位为ppm。此时点击“发布”按钮,按照系统提示一步一步进行下去就可以将刚刚创建的物模型属性发布到产品中。
<div align="center">
<img src=./../../../images/2_甲醛检测_发布物模型.png width=100%/>
</div>
&emsp;&emsp;
产品及其物模型创建完成后就可以创建这个产品的设备了。
<br>
### 3.4、创建云端设备(获取三元组)
&emsp;&emsp;
在产品列表页面中,点击”甲醛检测“后的“管理设备”,就会进到设备管理页面。
<div align="center">
<img src=./../../../images/2_甲醛检测_产品页_管理设备.png width=100%/>
</div>
&emsp;&emsp;
在“设备”页面点击“添加设备”按钮,如下图所示。
<div align="center">
<img src=./../../../images/1_添加设备入口.png width=100%/>
</div>
&emsp;&emsp;
在“添加设备”页面中设定“deviceName”,这里开发者可以自己填入自己想设定的设备名称,也可以不填任何内容让系统自动生成设备名称,如下图所示。
<div align="center">
<img src=./../../../images/1_添加设备.png width=40%/>
</div>
&emsp;&emsp;
设备添加完成后,点击“前往查看”按钮,就可以看到此设备端详细信息了。
<div align="center">
<img src=./../../../images/1_完成添加设备.png width=40%/>
</div>
&emsp;&emsp;
设备信息中有两个信息需要和设备端开发相匹配:
1. 三元组(点击下图中的“查看”及可看到三元组信息)
2. 物模型属性信息
<div align="center">
<img src=./../../../images/2_甲醛检测设备详情.png width=100%/>
</div>
<br>
#### 3.4.1、**获取设备三元组**
&emsp;&emsp;
如上图所示,点击“查看”按钮,就可以看到设备的三元组信息(如下图所示),三元组是物联网设备端和物联网云端设备相关联的唯一标识符,在设备端连接云端的时候会使用三元组信息和云端进行鉴权,鉴权通过之后云端会认为设备已激活并上线。
<div align="center">
<img src=./../../../images/1_设备三元组_马赛克.png width=50%/>
</div>
<br>
#### 3.4.2、**查看设备属性信息**
&emsp;&emsp;
设备详情信息页中的“物模型数据”标签页中可以看到设备的所有属性信息、设备事件上报情况及设备服务调用情况,如下图所示。待物联网设备按照设备属性对应的标识符上报设备属性的时候,本图片中的“甲醛浓度“属性值就会显示设备最新的属性信息。
<div align="center">
<img src=./../../../images/2_甲醛检测设备详情.png width=100%/>
</div>
<br>
> 创建产品和设备的过程是按照面向对象的思想进行设计的,其中创建产品可以看成是新建一个类,其中的物模型则是类的对象,创建设备则是进行类的实例化。
<br>
## 3、设备端开发
### 3.1、开发环境
在进行下一步之前请确保HaaS EDU K1开发环境已经搭建完毕。详情请参考[HaaS EDU K1开发环境](../../../startup/HaaS_EDU_K1_startup.md)的说明。
&emsp;&emsp;
该款甲醛传感器支持Uart和ADC两种方式,本案例中采用了ADC模式。
> 使用ADC模式前,请先将拨码开关切换到ADC一端。
&emsp;&emsp;
输出模拟电压(V)与浓度(ppm)是线性关系,0.4V对应0ppm, 2.0V对应5ppm,因此电压与浓度的线性关系图如下图所示:
<div align="center">
<img src=./../../../images/2_甲醛检测_DAC电压曲线.png width=80%/>
</div>
### 3.2、创建解决方案
&emsp;&emsp;
如下图所示,在Haas Studio中创建项目。先选择左侧的“开发板型号”再从右侧的案例中选择“甲醛检测系统”案例点击“立即创建”即可。
<div align="center">
<img src=./../../../images/HaaS_Studio_创建工程示范.png width=100%/>
</div>
<br>
> Python脚本的详细说明请参考脚本内嵌的文字版注释
1. **修改路由器名称及密码**
&emsp;&emsp;
修改main.py中wifiSsid和wifiPassword的值为读者实际要连接的路由器的名称及密码(请注意名称和密码都需要放在""符号中间)。
```python
# Wi-Fi SSID和Password设置
wifiSsid = "请填写您的路由器名称"
wifiPassword = "请填写您的路由器密码"
```
&emsp;&emsp;
修改完成之后get_wifi_status函数中的nm.connect(wifiSsid, wifiPassword) 语句就会连接读者自己设定的路由器。
2. **修改设备端三元组**
&emsp;&emsp;
修改本工程里main.py中productKey、deviceName和deviceSecret的值为读者创建的物联网设备的三元组信息。
```python
# 三元组信息
productKey = "产品密钥"
deviceName = "设备名称"
deviceSecret = "设备密钥"
```
1. **修改设备端上报数据所用标识符**
&emsp;&emsp;
main.py中下面的代码实现的是上传甲醛检测结果到云端的功能。其中HCHO便是甲醛检测结果上报云端所用的标识符。
```python
data = get_hcho_value()
H_str = "Hcho : " + str(round(data,2))+'ppm'
print('Hcho :' + str(round(data,2)) +'ppm')
# "HCHO" - 代表甲醛传感器测量到的浓度值
upload_data = {'params': ujson.dumps({
'HCHO': round(data,2),
})
}
# 上传甲醛浓度信息到物联网平台
device.postProps(upload_data)
# 每2秒钟上报一次
utime.sleep(2)
```
确保这个标识符和物联网产品的物模型中属性标识符是一样的,如下图所示:
<div align="center">
<img src=./../../../images/2_甲醛检测_属性标识符修改.png
width=100%/>
</div>
<br>
## 4、运行结果
### 4.1、本地查看
&emsp;&emsp;
推送此脚本到HaaS EDU K1之后并运行,串口会周期性的打印如下日志,其中:
* Wi-Fi connected 代表Wi-Fi连线成功
* Hcho:0.02ppm 代表检测到的甲醛浓度为0.02ppm
```log
>>> execfile("/data/pyamp/main.py")
...
Welcome to AliOS Things
amp shakehand begin...
dev /dev/wifi0 is already add
==== python execute from /data/pyamp/main.py ====
Welcome to MicroPython
...
Wi-Fi connected
DeviceIP:192.168.0.107
sleep for 1 s
...
success to establish tcp, fd=4
物联网平台连接成功
Hcho :0.02ppm
Hcho :0.02ppm
Hcho :0.02ppm
```
### 4.2、物联网平台端设备信息查看
&emsp;&emsp;
物联网设备的系统启动成功并连接到物联网平台之后,物联网平台上对应的设备状态会从”未激活状态“变为”上线“,在物模型数据标签页上会显示设备上报到物联网平台的属性值。
<div align="center">
<img src=./../../../images/2_甲醛检测_设备状态及属性.png width=100%/>
</div>
&emsp;&emsp;
此时如果开发板周围的甲醛浓度发生变化,物联网平台的物模型数据会更新为设备上报的最新的属性值。通过点击查看数据,可以看到一段时间监测到的甲醛浓度值。
<div align="center">
<img src=./../../../images/2_甲醛检测_查看数据.png width=100%/>
</div>
<br>
&emsp;&emsp;
到此为止,甲醛检测系统的案例就已经完成了。如果想要学习甲醛检测系统更详细的操作步骤,请参考“[甲醛检测系统详解](https://gitee.com/haasedu/haasedu/blob/release_2.0/2-%E6%99%BA%E8%83%BD%E7%94%9F%E6%B4%BB/%E5%9C%BA%E6%99%AF1-%E5%AE%88%E6%8A%A4%E5%AE%B6%E5%BA%AD%E5%81%A5%E5%BA%B7/%E7%94%B2%E9%86%9B%E6%A3%80%E6%B5%8B/README.md)”中的说明。
{
"name": "haasedu",
"version": "1.0.0",
"io": {
"hcho": {
"type": "ADC",
"port": 2,
"sampling": 12000000
},
"led_r": {
"type": "GPIO",
"port": 36,
"dir": "output",
"pull": "pullup"
},
"ap3216c": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 30
},
"mpu6050": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 105
},
"si7006": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 64
},
"spl06": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 119
},
"qmc5883": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 13
},
"ADC2": {
"type": "ADC",
"port": 0,
"sampling": 12000000
},
"GPIO2": {
"type": "GPIO",
"port": 2,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"GPIO3": {
"type": "GPIO",
"port": 3,
"dir": "output",
"pull": "pullup"
},
"GPIO4": {
"type": "GPIO",
"port": 4,
"dir": "output",
"pull": "pullup"
},
"GPIO5": {
"type": "GPIO",
"port": 5,
"dir": "output",
"pull": "pullup"
},
"GPIO6": {
"type": "GPIO",
"port": 6,
"dir": "output",
"pull": "pullup"
},
"GPIO7": {
"type": "GPIO",
"port": 7,
"dir": "output",
"pull": "pullup"
},
"GPIO19": {
"type": "GPIO",
"port": 19,
"dir": "output",
"pull": "pullup"
},
"GPIO22": {
"type": "GPIO",
"port": 22,
"dir": "output",
"pull": "pullup"
},
"KEY_1": {
"type": "GPIO",
"port": 23,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"KEY_2": {
"type": "GPIO",
"port": 20,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"KEY_3": {
"type": "GPIO",
"port": 21,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"KEY_4": {
"type": "GPIO",
"port": 26,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"I2C1": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 60
},
"led_g": {
"type": "GPIO",
"port": 35,
"dir": "output",
"pull": "pullup"
},
"led_b": {
"type": "GPIO",
"port": 34,
"dir": "output",
"pull": "pullup"
},
"SPI0": {
"type": "SPI",
"port": 0,
"mode": "master",
"freq": 2000000
},
"oled_spi": {
"type": "SPI",
"port": 1,
"mode": "master",
"freq": 26000000
},
"oled_dc": {
"type": "GPIO",
"port": 28,
"dir": "output",
"pull": "pullup"
},
"oled_res": {
"type": "GPIO",
"port": 30,
"dir": "output",
"pull": "pullup"
},
"serial2": {
"type": "UART",
"port": 2,
"dataWidth": 8,
"baudRate": 115200,
"stopBits": 1,
"flowControl": "disable",
"parity": "none"
}
},
"debugLevel": "ERROR",
"repl": "disable"
}
\ No newline at end of file
from driver import ADC
class HCHO(object):
def __init__(self, adcObj):
self.adcObj = None
if not isinstance(adcObj, ADC):
raise ValueError("parameter is not an ADC object")
self.adcObj = adcObj
def getPPM(self):
if self.adcObj is None:
raise ValueError("invalid ADC object")
min = 400
max = 0
value = 0
total = 0
i = 0
for i in range(32):
value = self.adcObj.readVoltage()
total += value
# print(value)
if (min >= value):
min = value
if (max <= value):
max = value
analogVoltage = (total - min - max) / 30
analogVoltage += 23
analogVoltage /= 1000.0
#linear relationship(0.4V for 0 ppm and 2V for 5ppm)
ppm = 3.125 * analogVoltage - 1.25
if ppm < 0 :
ppm = 0
return ppm
# -*- encoding: utf-8 -*-
from aliyunIoT import Device # iot组件是连接阿里云物联网平台的组件
import netmgr as nm # Wi-Fi功能所在库
import utime # 延时API所在组件
import ujson # json字串解析库
from driver import ADC # ADC类,通过微处理器的ADC模块读取ADC通道输入电压
import hcho # 甲醛hcho传感器类
adcObj = 0 # ADC通道对象
uartObj = 0 # UART通道对象
# 物联网平台连接标志位
iot_connected = False
# Wi-Fi SSID和Password设置
wifiSsid = "请填写您的路由器名称"
wifiPassword = "请填写您的路由器密码"
# 三元组信息
productKey = "产品密钥" #需要填入物联网云平台申请到的productKey信息
deviceName = "设备名称" #需要填入物联网云平台申请到的deviceName信息
deviceSecret = "设备密钥" #需要填入物联网云平台申请到的deviceSecret信息
# 物联网设备实例
device = None
hchoDev = 0
def hcho_init():
global adcObj,hchoDev
adcObj = ADC()
adcObj.open("hcho")
hchoDev = hcho.HCHO(adcObj)
def get_hcho_value():
global hchoDev
return hchoDev.getPPM()
# 等待Wi-Fi成功连接到路由器
def get_wifi_status():
nm.init()
nm.disconnect()
wifi_connected = nm.getStatus()
print("start to connect " , wifiSsid)
nm.connect(wifiSsid, wifiPassword) # 连接到指定的路由器(路由器名称为wifiSsid, 密码为:wifiPassword)
while True :
if wifi_connected == 5: # nm.getStatus()返回5代表连线成功
break
else:
wifi_connected = nm.getStatus() # 获取Wi-Fi连接路由器的状态信息
utime.sleep(0.5)
print("wifi_connected: " , wifi_connected)
print("Wi-Fi connected")
print('DeviceIP:' + nm.getInfo()['ip']) # 打印Wi-Fi的IP地址信息
# 物联网平台连接成功的回调函数
def on_connect(data):
global iot_connected
iot_connected = True
# 设置props 事件接收函数(当云平台向设备下发属性时)
def on_props(request):
pass
# 连接物联网平台
def connect_lk(productKey, deviceName, deviceSecret):
global device, iot_connected
key_info = {
'region': 'cn-shanghai',
'productKey': productKey,
'deviceName': deviceName,
'deviceSecret': deviceSecret,
'keepaliveSec': 60
}
# 将三元组信息设置到iot组件中
device = Device()
# 设定连接到物联网平台的回调函数,如果连接物联网平台成功,则调用on_connect函数
device.on(Device.ON_CONNECT, on_connect)
# 配置收到云端属性控制指令的回调函数,如果收到物联网平台发送的属性控制消息,则调用on_props函数
device.on(Device.ON_PROPS, on_props)
# 启动连接阿里云物联网平台过程
ret = device.connect(key_info)
# 等待设备成功连接到物联网平台
while(True):
if iot_connected:
print('物联网平台连接成功')
break
else:
print('sleep for 1 s')
utime.sleep(1)
# 上传甲醛浓度信息到物联网平台
def upload_hcho_detector_state():
global device
# 无限循环
while True:
data = get_hcho_value()
H_str = "Hcho : " + str(round(data,2))+'ppm'
print('Hcho :' + str(round(data,2)) +'ppm')
# "HCHO" - 代表甲醛传感器测量到的浓度值
upload_data = {'params': ujson.dumps({
'HCHO': round(data,2),
})}
# 上传甲醛浓度信息到物联网平台
device.postProps(upload_data)
# 每2秒钟上报一次
utime.sleep(2)
if __name__ == '__main__':
# 运行此demo之前务必保证模块已经上电5分钟以上
hcho_init()
# 请替换物联网平台申请到的产品和设备信息,可以参考文章:https://blog.csdn.net/HaaSTech/article/details/114360517
# global productKey, deviceName, deviceSecret ,on_request, on_play
get_wifi_status()
connect_lk(productKey, deviceName, deviceSecret)
upload_hcho_detector_state()
# 起夜灯
&emsp;&emsp;
下图是本案例除硬件连线外的2步导学,每个步骤中实现的功能请参考图中的说明。
<div align="center">
<img src=./../../../images/2_起夜灯_步骤概述.jpg width=60%/>
</div>
## 1、简介
### 1.1、背景
&emsp;&emsp;
**红外线**是一种人类肉眼看不到的光,虽然看不见,但他有一个显著的特性就是具有热效应,即所有高于绝对零度的物质都会产生红外线。人体热释电红外传感器就是一种能探测人体红外光谱变化的传感器,他能检测人发射的红外线,并转化为电信号输出。
&emsp;&emsp;
使用一个控制器来接收人体热释电红外传感器的信号,通过这个信号来控制灯的开启和关闭,就可以制作一个实用的起夜灯。
<div align="center">
<img src=./../../../images/2_起夜灯_起夜灯原理.png width=60%/>
</div>
<br>
### 1.1、准备
* [HaaS EDU K1开发板](https://haas.iot.aliyun.com/solution/detail/hardware?versionId=800C5AB3B8A4A88800000001&dataId=800C5AB3B8A4A888) 一套
* [人体热释电红外传感器](https://haas.iot.aliyun.com/solution/detail/hardware?versionId=800CE1DD3CE998E000000002&dataId=800CE1DD3CE998E0) 一个
* 连接线 若干
&emsp;&emsp;
硬件连线图如下图所示:
<div align="center">
<img src=./../../../images/2_2_HaaS_EDU_K1_起夜灯硬件接线图.png width=85%/>
</div>
<br>
## 2、物联网平台开发
&emsp;&emsp;
对于第一次使用物联网平台的读者,需要开通实例以使用物联网平台的功能。这里可以使用免费的公共实例进行开发。
&emsp;&emsp;
[物联网平台](https://iot.console.aliyun.com/lk/summary/new)中,左上角选择“华东2-上海”,点击“公共实例”,即可开通。
<div align="center">
<img src=./../../../images/5_3_开通公共实例.png
width=100%/>
</div>
&emsp;&emsp;
开通物联网平台功能之后,需要完成下面的3个步骤完成云端设备的创建:
1. 创建云端产品
2. 创建产品属性(物模型)
3. 创建云端设备(获取三元组)
<br>
### 2.1、创建云端产品
&emsp;&emsp;
点击上图中的“公共实例”,即可进入[控制台](https://iot.console.aliyun.com/lk/summary/new)进行产品创建。然后,点击创建产品按钮,如下图所示。
<div align="center">
<img src=./../../../images/1_创建产品.png
width=60%/>
</div>
&emsp;&emsp;
在新建产品设定页面按照下图所示,设定“产品名称”,选择所属的“自定义品类”(自定义品类的物模型为空,需要自己创建,也可以通过导入外部物模型的方式导入),节点类型选择“直连设备”,联网方式选择“Wi-Fi”,数据格式选择“ICA标准数据格式”,检验类型和认证方式选择默认设定即可。还可以根据开发者自己的需求在“产品描述”页面添加针对此产品的描述。
<div align="center">
<img src=./../../../images/2_2_新建起夜灯设备.png
width=60%/>
</div>
&emsp;&emsp;
选择之后,点击“确认”按钮,即可完成产品创建。返回“产品”页面之后可以看到产品类表中会出现刚刚创建的“起夜灯”的产品,如下图所示。
<div align="center">
<img src=./../../../images/2_2_起夜灯_产品列表页.png width=60%/>
</div>
<br>
### 2.2、创建产品属性(物模型)
&emsp;&emsp;
点击上图中的“查看”按钮,即可看到产品信息,Topic列表,功能定义,数据解析等跟产品相关功能的设定。点开“功能定义”标签页,可以看到设备物模型定义。
<div align="center">
<img src=./../../../images/2_2_起夜灯_产品详情页面.png width=60%/>
</div>
&emsp;&emsp;
其中标识符是设备端上报设备属性状态的消息中需要使用的标识符,并且只有在设备上报的属性内容符合“数据定义”中的数据取值范围的时候才会被物联网平台记录,否则会被物联网平台认定为非法属性而过滤掉。
&emsp;&emsp;
本节我们选择直接新建方式来创建此产品需要的物模型信息,点击上图中的”编辑草稿“按钮。然后如下图再点击“添加自定义功能”。
<div align="center">
<img src=./../../../images/2_2_起夜灯_编辑草稿.png width=60%/>
</div>
&emsp;&emsp;
如下图添加一个“LED开关”的默认功能。其中标识符为LEDSwith, 0表示LED灯关闭,1表示LED开启。
<div align="center">
<img src=./../../../images/2_2_起夜灯_添加自定义功能.png width=60%/>
</div>
点击确定后回到前一个页面,再点击“发布上线”
<div align="center">
<img src=./../../../images/2_2_起夜灯_发布物模型.png
width=60%/>
</div>
&emsp;&emsp;
产品及其物模型创建完成后就可以创建这个产品的设备了。
<br>
### 2.3、创建云端设备(获取三元组)
&emsp;&emsp;
在产品列表页面中,点击”起夜灯“后的“管理设备”,就会进到设备管理页面。
<div align="center">
<img src=./../../../images/2_2_起夜灯_产品页_管理设备.png width=100%/>
</div>
&emsp;&emsp;
在“设备”页面点击“添加设备”按钮,如下图所示。
<div align="center">
<img src=./../../../images/2_2_起夜灯_添加设备入口.png width=80%/>
</div>
&emsp;&emsp;
在“添加设备”页面中设定“deviceName”,这里开发者可以自己填入自己想设定的设备名称,也可以不填任何内容让系统自动生成设备名称,如下图所示。
<div align="center">
<img src=./../../../images/2_2_起夜灯_添加设备.png width=40%/>
</div>
&emsp;&emsp;
设备添加完成后,点击“前往查看”按钮,就可以看到此设备端详细信息了。
<div align="center">
<img src=./../../../images/2_2_起夜灯_完成添加设备.png width=40%/>
</div>
&emsp;&emsp;
设备信息中有两个信息需要和设备端开发相匹配:
1. 三元组
2. 物模型属性信息
<div align="center">
<img src=./../../../images/2_2_起夜灯_设备详情.png width=100%/>
</div>
<br>
#### 2.3.1、**获取设备三元组**
&emsp;&emsp;
如上图所示,点击deviceSecret后面的“查看”按钮,就可以看到设备的三元组信息,三元组是物联网设备端和物联网云端设备相关联的唯一标识符,在设备端连接云端的时候会使用三元组信息和云端进行鉴权,鉴权通过之后云端会认为设备已激活并上线。
<div align="center">
<img src=./../../../images/2_2_起夜灯_设备三元组.png width=50%/>
</div>
<br>
#### 2.3.2、**查看设备属性信息**
&emsp;&emsp;
设备详情信息页中的“物模型数据”标签页中可以看到设备的所有属性信息、设备时间上报情况及设备服务调用情况,如下图所示。待物联网设备按照设备属性对应的标识符上报设备属性的时候,本图片中的“LED开关“,属性值就会显示设备最新的属性信息。
<div align="center">
<img src=./../../../images/2_2_起夜灯_设备物模型数据.png width=80%/>
</div>
<br>
> 创建产品和设备的过程是按照面向对象的思想进行设计的,其中创建产品可以看成是新建一个类,其中的物模型则是类的对象,创建设备则是进行类的实例化。
<br>
## 3、设备端开发
### 3.1、开发环境
&emsp;&emsp;
在进行下一步之前请确保HaaS EDU K1开发环境已经搭建完毕。详情请参考[HaaS EDU K1快速开始](../../../startup/HaaS_EDU_K1_startup.md)的说明。
<br>
### 3.2、创建解决方案
&emsp;&emsp;
如下图所示,在Haas Studio中创建项目。先选择左侧的“开发板型号”再从右侧的案例中选择“起夜灯”案例点击“立即创建”即可。
<div align="center">
<img src=./../../../images/HaaS_Studio_创建工程示范.png width=100%/>
</div>
<br>
> Python脚本的详细说明请参考脚本内嵌的文字注释
1. **修改路由器名称及密码**
&emsp;&emsp;
修改main.py中wifiSsid和wifiPassword的值为读者实际要连接的路由器的名称及密码(请注意名称和密码都需要放在""符号中间)。
```python
# Wi-Fi SSID和Password设置
wifiSsid = "请输入您的路由器名称"
wifiPassword = "请输入您的路由器密码"
```
&emsp;&emsp;
修改完成之后get_wifi_status函数中的nm.connect(wifiSsid, wifiPassword) 语句就会连接读者自己设定的路由器。
2. **修改设备端三元组**
&emsp;&emsp;
修改main.py中productKey、deviceName和deviceSecret的值为读者创建的物联网设备的三元组信息。
```python
# 三元组信息
productKey = "产品密钥"
deviceName = "设备名称"
deviceSecret = "设备密钥"
```
3. **修改设备端上报数据所用标识符**
&emsp;&emsp;
main.py中下面的代码实现的是上传起夜灯状态到云端的功能。其中LEDSwitch便是起夜灯状态上报云端所用的标识符。
```python
while True :
have_human = irDev.irDetect() # 获取传感器的值
if (have_human == 1) :
print("human is here .....\r\n")
if (last_have_human != have_human) :
led_control(have_human) # 控制LED亮灭
# 生成上报到物联网平台的属性值字串,此处的属性标识符"LEDSwith"必须和物联网平台的属性一致
# "LEDSwitch" - 表示起夜灯的状态
upload_data = {'params': ujson.dumps({
'LEDSwitch': have_human,
})
}
# 上传LED状态到物联网平台
device.postProps(upload_data)
last_have_human = have_human # 记录当前状态
utime.sleep(1) # 休眠1秒
```
&emsp;&emsp;
确保这起夜灯的标识符和物联网产品的物模型中属性标识符是一样的.
<br>
## 4、运行结果
### 4.1、本地查看
&emsp;&emsp;
推送此脚本到HaaS EDU K1之后,串口会周期性的打印如下日志,其中:
• “物联网平台连接成功” 代表成功连接到物联网平台
• 人体感应传感器检测到有人体活动的时候变会打印“human is here”,同时HaaS EDU K1上面的红色LED会亮起。
• 人体感应传感器没检测到人体活动的时候变会停止打印“human is here”,Haas EDU K1的红色LED灯也会随之熄灭。
```log
...
Welcome to AliOS Things
amp shakehand begin...
dev /dev/wifi0 is already add
==== python execute from /data/pyamp/main.py ====
Welcome to MicroPython
...
Wi-Fi connected
DeviceIP:192.168.0.107
sleep for 1 s
...
success to establish tcp, fd=4
物联网平台连接成功
human is here ...
human is here ...
human is here ...
...
```
<br>
### 4.2、物联网平台端设备信息查看
&emsp;&emsp;
物联网设备的系统启动成功并连接到物联网平台之后,物联网平台上对应的设备状态会从”未激活状态“变为”上线“,在物模型数据标签页上会显示设备上报到物联网平台的属性值。
<div align="center">
<img src=./../../../images/2_2_起夜灯_设备状态及属性.png width=100%/>
</div>
&emsp;&emsp;
此时如果开发板周围有人经过,起夜灯就会开启,物联网平台的物模型数据会更新为设备上报的最新的属性值。
<br>
&emsp;&emsp;
到此为止,起夜灯数据上云的案例就已经完成了。如果想要学习起夜灯案例更详细的操作步骤,请参考“[起夜灯系统详解](https://gitee.com/haasedu/haasedu/blob/release_2.0/2-%E6%99%BA%E8%83%BD%E7%94%9F%E6%B4%BB/%E5%9C%BA%E6%99%AF2-%E8%B5%B7%E5%A4%9C%E7%81%AF/README.md)”中的说明。
{
"name": "haasedu",
"version": "1.0.0",
"io": {
"ir": {
"type": "GPIO",
"port": 7,
"dir": "input",
"pull": "pullup"
},
"led": {
"type": "GPIO",
"port": 36,
"dir": "output",
"pull": "pullup"
},
"led_r": {
"type": "GPIO",
"port": 36,
"dir": "output",
"pull": "pullup"
},
"ap3216c": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 30
},
"mpu6050": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 105
},
"si7006": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 64
},
"spl06": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 119
},
"qmc5883": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 400000,
"mode": "master",
"devAddr": 13
},
"ADC2": {
"type": "ADC",
"port": 0,
"sampling": 12000000
},
"GPIO2": {
"type": "GPIO",
"port": 2,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"GPIO3": {
"type": "GPIO",
"port": 3,
"dir": "output",
"pull": "pullup"
},
"GPIO4": {
"type": "GPIO",
"port": 4,
"dir": "output",
"pull": "pullup"
},
"GPIO5": {
"type": "GPIO",
"port": 5,
"dir": "output",
"pull": "pullup"
},
"GPIO6": {
"type": "GPIO",
"port": 6,
"dir": "output",
"pull": "pullup"
},
"GPIO7": {
"type": "GPIO",
"port": 7,
"dir": "output",
"pull": "pullup"
},
"GPIO19": {
"type": "GPIO",
"port": 19,
"dir": "output",
"pull": "pullup"
},
"GPIO22": {
"type": "GPIO",
"port": 22,
"dir": "output",
"pull": "pullup"
},
"KEY_1": {
"type": "GPIO",
"port": 23,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"KEY_2": {
"type": "GPIO",
"port": 20,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"KEY_3": {
"type": "GPIO",
"port": 21,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"KEY_4": {
"type": "GPIO",
"port": 26,
"dir": "irq",
"pull": "pullup",
"intMode": "rising"
},
"I2C1": {
"type": "I2C",
"port": 1,
"addrWidth": 7,
"freq": 100000,
"mode": "master",
"devAddr": 60
},
"led_g": {
"type": "GPIO",
"port": 35,
"dir": "output",
"pull": "pullup"
},
"led_b": {
"type": "GPIO",
"port": 34,
"dir": "output",
"pull": "pullup"
},
"SPI0": {
"type": "SPI",
"port": 0,
"mode": "master",
"freq": 2000000
},
"oled_spi": {
"type": "SPI",
"port": 1,
"mode": "master",
"freq": 26000000
},
"oled_dc": {
"type": "GPIO",
"port": 28,
"dir": "output",
"pull": "pullup"
},
"oled_res": {
"type": "GPIO",
"port": 30,
"dir": "output",
"pull": "pullup"
},
"serial2": {
"type": "UART",
"port": 2,
"dataWidth": 8,
"baudRate": 115200,
"stopBits": 1,
"flowControl": "disable",
"parity": "none"
}
},
"debugLevel": "ERROR",
"repl": "disable"
}
\ No newline at end of file
from driver import GPIO
class IR(object):
def __init__(self, gpioObj):
self.gpioObj = None
if not isinstance(gpioObj, GPIO):
raise ValueError("parameter is not a GPIO object")
self.gpioObj = gpioObj
def irDetect(self):
if self.gpioObj is None:
raise ValueError("invalid GPIO object")
value = self.gpioObj.read()
return value
# -*- coding: UTF-8 -*-
from aliyunIoT import Device # aliyunIoT组件是连接阿里云物联网平台的组件
import netmgr as nm # Wi-Fi功能所在库
import ujson # json字串解析库
import utime # 延时API所在组件
from driver import GPIO # GPIO类,用于控制微处理器的输入输出功能
import ir # ir人体红外传感器类
gpioirDev = 0
gpioledDev = 0
irDev = 0
# 物联网平台连接标志位
iot_connected = False
wlan = None
# Wi-Fi SSID和Password设置
wifiSsid = "请输入您的路由器名称"
wifiPassword = "请输入您的路由器密码"
# 三元组信息
productKey = "产品密钥"
deviceName = "设备名称"
deviceSecret = "设备密钥"
# 物联网设备实例
device = None
# 等待Wi-Fi成功连接到路由器
def get_wifi_status():
nm.init()
nm.disconnect()
wifi_connected = nm.getStatus()
print("start to connect " + wifiSsid)
nm.connect(wifiSsid, wifiPassword) # 连接到指定的路由器(路由器名称为wifiSsid, 密码为:wifiPassword)
while True :
if wifi_connected == 5: # nm.getStatus()返回5代表连线成功
break
else:
wifi_connected = nm.getStatus() # 获取Wi-Fi连接路由器的状态信息
utime.sleep(0.5)
print("wifi_connected: " + str(wifi_connected))
print("Wi-Fi connected")
print('DeviceIP:' + nm.getInfo()['ip']) # 打印Wi-Fi的IP地址信息
# 物联网平台连接成功的回调函数
def on_connect(data):
global iot_connected
iot_connected = True
# 设置props 事件接收函数(当云平台向设备下发属性时)
def on_props(request):
pass
def connect_lk(productKey, deviceName, deviceSecret):
global device, iot_connected
key_info = {
'region': 'cn-shanghai',
'productKey': productKey,
'deviceName': deviceName,
'deviceSecret': deviceSecret,
'keepaliveSec': 60
}
# 将三元组信息设置到iot组件中
device = Device()
# 设定连接到物联网平台的回调函数,如果连接物联网平台成功,则调用on_connect函数
device.on(Device.ON_CONNECT, on_connect)
# 配置收到云端属性控制指令的回调函数,如果收到物联网平台发送的属性控制消息,则调用on_props函数
device.on(Device.ON_PROPS, on_props)
# 启动连接阿里云物联网平台过程
device.connect(key_info)
# 等待设备成功连接到物联网平台
while(True):
if iot_connected:
print('物联网平台连接成功')
break
else:
print('sleep for 1 s')
utime.sleep(1)
print('sleep for 2s')
utime.sleep(2)
def human_check_init():
global gpioirDev,gpioledDev,irDev
gpioirDev = GPIO()
gpioirDev.open("ir")
irDev = ir.IR(gpioirDev)
gpioledDev = GPIO()
gpioledDev.open("led")
def led_control(on):
global gpioledDev
if on == 0:
gpioledDev.write(0) # GPIO写入0,灭灯
else:
gpioledDev.write(1) # GPIO写入1,亮灯
def human_detector() :
global gpioirDev,gpioledDev,irDev
last_have_human = 0 # 记录初始值
while True :
have_human = irDev.irDetect() # 获取传感器的值
if (have_human == 1) :
print("human is here ...\r\n")
if (last_have_human != have_human) :
led_control(have_human) # 控制LED亮灭
# 生成上报到物联网平台的属性值字串,此处的属性标识符"LEDSwith"必须和物联网平台的属性一致
# "LEDSwitch" - 表示起夜灯的状态
upload_data = {'params': ujson.dumps({
'LEDSwitch': have_human,
})
}
# 上传LED状态到物联网平台
device.postProps(upload_data)
last_have_human = have_human # 记录当前状态
utime.sleep(1) # 休眠1秒
gpioirDev.close()
gpioledDev.close()
if __name__ == '__main__' :
# 请替换物联网平台申请到的产品和设备信息,可以参考文章:https://blog.csdn.net/HaaSTech/article/details/114360517
# global productKey, deviceName, deviceSecret ,on_request, on_play
get_wifi_status()
connect_lk(productKey, deviceName, deviceSecret)
human_check_init()
human_detector()
# 智慧路灯系统
&emsp;&emsp;
下图是本案例除硬件连线外的2步导学,每个步骤中实现的功能请参考图中的说明。在硬件连线完成之后我们建议您先使用“一分钟上云体验”功能预先体验本案例的实际运行效果。
<div align="center">
<img src=./../../../images/4_智慧路灯_步骤概述.jpg width=60%/>
</div>
## 1、简介
&emsp;&emsp;
随着城市化不断扩大,城市的灯光秀越来越多,让我们居住的城市生活变得五彩缤纷,灯火辉煌,让城市显得越来越繁华。但是,我们也会经常发现有些路边的灯,在天很黑了不会自动打开,或者到清晨,天很亮了,马路边的灯也不会自动熄灭,只会在固定时间统一开关控制,这样既不人性化也不环保。于是现在很多地方的路灯会根据周围环境亮度自动开启或关闭路边灯光,那这样的路灯就非常人性化,同时也非常环保,这才是真正的城市智慧路灯。
### 1.1、背景知识
&emsp;&emsp;
本章课程打造的智慧路灯控制系统是当检测到周围环境亮度变暗的时候,系统会自动打开灯,当周围环境亮度变亮的时候,系统就会自动关闭灯。本智慧路灯控制系统默认设置一个亮度阈值,当亮度值超过阈值,关灯,当亮度值低于阈值,开灯,当然,这个阈值可以根据每个人对光的亮度感觉不同而调整。
### 1.2、准备
&emsp;&emsp;
本案例之需要HaaS EDU K1开发一套。实验过程中会使用HaaS EDU K1内置的AP3216C传感器和红色LED。
## 2、物联网平台开发
### 2.1、开通公共实例
&emsp;&emsp;
对于第一次使用物联网平台的读者,需要开通实例以使用物联网平台的功能。这里可以使用免费的公共实例进行开发。
&emsp;&emsp;
[物联网平台](https://iot.console.aliyun.com/lk/summary/new)中,左上角选择“华东2-上海”,点击“公共实例”,即可开通。
<div align="center">
<img src=./../../../images/5_3_开通公共实例.png
width=100%/>
</div>
### 2.2、云端创建产品
1. **创建产品**
&emsp;&emsp;
点击上图中的“公共实例”,即可进入[控制台](https://iot.console.aliyun.com/lk/summary/new)进行产品创建。然后,点击创建产品按钮,如下图所示。
<div align="center">
<img src=./../../../images/2_3_创建产品.jpg
width=40%/>
</div>
&emsp;&emsp;
这里创建了一个名称为“智慧路灯”的产品。
<div align="center">
<img src=./../../../images/5_1_新建智慧路灯产品.jpg
width=40%/>
</div>
&emsp;&emsp;
点击确认,就可以在产品列表中出现智慧路灯。
<div align="center">
<img src=./../../../images/5_1_完成产品创建.jpg
width=40%/>
</div>
&emsp;&emsp;
查看产品详情。
<div align="center">
<img src=./../../../images/5_1_产品功能定义.jpg
width=40%/>
</div>
&emsp;&emsp;
编辑草稿,选择自定义功能。
<div align="center">
<img src=./../../../images/5_1_自定义产品物模型定义.jpg
width=40%/>
</div>
&emsp;&emsp;
如下图设置对应产品的物模型,分别设置光强度电压值和灯开关两个模型。
<div align="center">
<img src=./../../../images/5_1_2产品物模型.jpg
width=40%/>
<img src=./../../../images/5_1_3产品物模型onoff.jpg
width=40%/>
</div>
&emsp;&emsp;
在产品详情中增加产品的功能,可以看到刚才设置的光强度电压值和灯开关两个功能属性,点击发布上线。
<div align="center">
<img src=./../../../images/5_1_3完成物模型设置.jpg
width=80%/>
</div>
&emsp;&emsp;
这样,整个智慧路灯产品在物联网平台也创建好了。
2. **设备管理**
&emsp;&emsp;
选中最左边栏设备管理中的设备,然后添加设备,
<div align="center">
<img src=./../../../images/2_3_添加设备.jpg
width=40%/>
</div>
&emsp;&emsp;
选择的产品为刚刚创建的智慧路灯,设备名设置为smartlight,点击确认,
<div align="center">
<img src=./../../../images/5_1_智慧路灯设备.jpg
width=40%/>
</div>
&emsp;&emsp;
进入设备详情,获取设备三元组信息,
<div align="center">
<img src=./../../../images/5_1_设备三元组信息.jpg
width=40%/>
</div>
&emsp;&emsp;
将设备三元组信息一键拷贝出来,在设备开发中会用到。
<div align="center">
<img src=./../../../images/2_3_设备证书.jpg
width=40%/>
</div>
3. **设备引擎**
&emsp;&emsp;
如何通过光感自适应控制灯的亮灭呢?可以在物联网平台上通过引擎规则实现,本课程使用的光强度传感器AP3216C返回值越低,说明周围的环境光强度越低;反之,返回值越高,说明周围的环境光强度越高。
<div align="center">
<img src=./../../../images/5_1_haaseduk1_规则引擎1.jpg
width=70%/>
</div>
&emsp;&emsp;
当光强度大于100,说明周围的环境变亮,执行关灯的指令。<br />
<div align="center">
<img src=./../../../images/5_1_haaseduk1_规则引擎2.jpg
width=70%/>
</div>
&emsp;&emsp;
当光强度电压值小于100,说明周围的环境变暗,执行开灯的指令。<br />
<div align="center">
<img src=./../../../images/5_1_haaseduk1_规则引擎3.jpg
width=70%/>
</div>
&emsp;&emsp;
规则引擎创建完成以后,启动引擎即可。<br />
<div align="center">
<img src=./../../../images/5_1_haaseduk1_规则引擎4.jpg
width=70%/>
</div>
## 3、设备端开发
### 3.1、开发环境
&emsp;&emsp;
在进行下一步之前请确保HaaS EDU K1开发环境已经搭建完毕。详情请参考“[HaaS EDU K1快速开始](https://hli.aliyuncs.com/haas-static/haasapi/index.html#/Python/docs/zh-CN/startup/HaaS_EDU_K1_startup)”的说明。
### 3.2、创建解决方案
&emsp;&emsp;
如下图所示,在Haas Studio中创建项目。先选择左侧的“开发板型号”再从右侧的案例中选择“智能路灯系统”案例点击“立即创建”即可。
<div align="center">
<img src=./../../../images/HaaS_Studio_创建工程示范.png width=100%/>
</div>
<br>
> Python脚本的详细说明请参考脚本内嵌的文字注释
&emsp;&emsp;
之后对代码进行如下修改。
1. **修改路由器名称及密码**
&emsp;&emsp;
修改main.py中wifiSsid和wifiPassword的值为读者实际要连接的路由器的名称及密码(请注意名称和密码都需要放在""符号中间)。
```python
# wifi连接的的wifiSsid和wifiPassword定义
wifiSsid = "请填写您的路由器名称"
wifiPassword = "请填写您的路由器密码"
```
&emsp;&emsp;
修改完成之后get_wifi_status函数中的nm.connect(wifiSsid, wifiPassword) 语句就会连接读者自己设定的路由器。
1. **修改设备端三元组**
&emsp;&emsp;
修改main.py中productKey、deviceName和deviceSecret的值为上面物联网平台创建的物联网设备的三元组信息。
```python
# 物联网平台相关的key和serect定义
productKey = "产品密钥"
deviceName = "设备名"
deviceSecret = "设备密钥"
```
3. **修改设备端上报光强度和下发开关标识符**
&emsp;&emsp;
main.py中下面的代码实现的是上传光强度值到云端的功能,以及云端下发对应的开关命令。其中Brightness便是光强度值上报时所用的标识符,onoff为云端下发的开关标识符。
```python
def report_light_data(l_data):
# 生成上报到物联网平台的属性值字串
prop = ujson.dumps({
'Brightness': l_data,
})
upload_data = {'params': prop}
# 上传光强度信息到物联网平台
device.postProps(upload_data)
```
&emsp;&emsp;
下面是采集光照数据的时候,采样值是跨阈值100就需要进行上报。
```python
# 采集打印光照强度电压值
def show_lightness():
global lightness,i_light,last_light,system_reset
i_light = int(lightness)
if system_reset == 1:
print("system first come")
system_reset = 0 # 系统第一次启动
report_light_data(i_light)
elif (i_light >= 1000 and last_light < 1000) or (i_light < 1000 and last_light >= 1000):
report_light_data(i_light)
print("light has change")
else:
print('no need report')
if i_light < 10:
T_str = "亮度:" + str(round(i_light, 1))
elif i_light >= 10 and i_light < 100:
T_str = "亮度:" + str(round(i_light, 2))
elif i_light >= 100 and i_light < 1000:
T_str = "亮度:" + str(round(i_light, 3))
elif i_light >= 1000 and i_light < 10000:
T_str = "亮度:" + str(round(i_light, 4))
print(T_str)
last_light = i_light
```
&emsp;&emsp;
处理物联网平台过来的数据。
```python
# 设置props 事件接收函数(当云平台向设备下发属性时)
def on_props(request):
global alarm_on, device
print(request)
payload = ujson.loads(request['params'])
# 获取dict状态字段 注意要验证键存在 否则会抛出异常
if "onoff" in payload.keys():
alarm_on = payload["onoff"]
if (alarm_on):
print("开灯")
ledOn()
else:
print("关灯")
ledOff()
# 要将更改后的状态同步上报到云平台
upload_data = {'params': ujson.dumps({
'onoff': alarm_on,
})
}
# 上报本地报警灯状态到云端
device.postProps(upload_data)
```
&emsp;&emsp;
确保Brightness和onoff两个标识符和物联网产品的物模型中属性标识符是一样的,如下图所示:
<div align="center">
<img src=./../../../images/5_1_完成物模型设置.jpg
width=40%/>
</div>
## 4、运行结果
&emsp;&emsp;
推送此脚本到HaaS EDU K1之后,设备上打印光强度值,同时将相关的光强度值上传到云端,并且当光强度大于100,打开HaaS EDU K1开发板上的LED灯;当光强度小于100,关闭HaaS EDU K1开发板上的LED灯,如下日志所示:
```bash
...
物联网平台连接成功
...
ap3216c init finished
system first come
...
uploading data to the cloud: {'params': '{"Brightness": 304}'}
亮度:304
led off
uploading data to the cloud: {'params': '{"Brightness": 0}'}
light has change
亮度:0
led on
no need report
亮度:53
uploading data to the cloud: {'params': '{"Brightness": 302}'}
light has change
亮度:302
led off
uploading data to the cloud: {'params': '{"Brightness": 5}'}
light has change
亮度:5
led on
...
```
&emsp;&emsp;
到此为止,智慧路灯系统案例就已经完成了。如果想要学习智慧路灯实验更详细的操作步骤,请参考“[智慧路灯系统详解](https://gitee.com/haasedu/haasedu/blob/release_2.0/5-%E6%99%BA%E6%85%A7%E5%9F%8E%E5%B8%82/%E5%9C%BA%E6%99%AF1-%E6%99%BA%E6%85%A7%E8%B7%AF%E7%81%AF/README.md)”中的说明。
from driver import UART
from micropyGNSS import MicropyGNSS
class Gnss(object):
def __init__(self, uartObj):
self.uartObj = None
if not isinstance(uartObj, UART):
raise ValueError("parameter is not a GPIO object")
# 初始化定位模组串口
self.uartObj = uartObj
self.gnss = MicropyGNSS(location_formatting='dd')
def getLocation(self):
if self.uartObj is None:
raise ValueError("invalid UART object")
# 创建定位信息解析器
sentence = bytearray(100)
recvsize = self.uartObj.read(sentence)
if(recvsize):
print(sentence)
sentence = sentence.decode()
# 解析地理位置信息
for c in sentence:
self.gnss.update(c)
print(self.gnss.longitude, self.gnss.latitude, self.gnss.altitude)
return self.gnss
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册