HaaS506传感器数据上云和继电器控制

1. 前言

本实验将向大家介绍如何使用Python语言在HaaS 506上实现读取传感信息,上报到阿里云物联网平台,并实现云端下发控制继电器

2. 开发环境

3. 涉及知识点

  1. uart通讯原理

    A串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数 据一个一个的顺序传输,通信线路简单。使用两条线即可实现双向通信,一条用于发送,一条用于接收。串口通信距离远,但是速度相对会低,串口是一种很常用的工业接口。

  2. 空气传感器,TB600B

    TB600B 系列 SMELL异味检测模组汇集了诸多来自德国的高精度检测技术,以及德国团队的设计理念,核心传感器采用全球电化学领域体积最小的德国 EC Sense 固态聚合物传感器。可以替代我们的鼻子夫精准嗅出气体浓度,实现精准监测。UART数字式信号输出,省去了客户对传感器应用的了解,以及校准的繁琐工作。应用领域:家居装修材料释放异味检测、养殖场环境恶臭检测、空气新鲜测量评估、办公楼宇及公共空间异味检测、污水恶臭,垃圾恶臭检测、厕所环境异味检测、口腔异味检测、汽车装饰材料异味检测、纺织品异味检测。

  3. 阿里云物联网平台

    阿里云物联网平台为设备提供安全可靠的连接通信能力,向下连接海量设备,支撑设备数据采集上云;向上提供云端API,服务端通过调用云端API将指令下发至设备端,实现远程控制。

4. 代码实现

代码流程图
  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
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# coding=utf-8
# This is a sample Python script.
import iot
import ujson
import network
import utime as time
from driver import GPIO
from driver import UART

t1 = 30
gas_threshold = 5.0
liq_mdcn_alarm = False
gas_alarm = False
version = 'v0.0.1'
uart1 = UART('serail1')
liq_level = GPIO()
gpio = GPIO()
'''0 1 means cloud ctrl,2 local ctrl'''
cloud_ctrl = 2
g_connect_status = False
ini_file_name = '/user/cfg.txt'

def on_4g_cb(args):
    global g_connect_status
    pdp = args[0]
    netwk_sta = args[1]
    if netwk_sta == 1:
        g_connect_status = True
    else:
        g_connect_status = False


def connect_network():
    global on_4g_cb,g_connect_status

    net = network.NetWorkClient()
    g_register_network = False
    if net._stagecode is not None and net._stagecode == 3 and net._subcode == 1:
        g_register_network = True
    else:
        g_register_network = False
    if g_register_network:
        net.on(1,on_4g_cb)
        net.connect(None)
    else:
        print('connect network failed')
    for i in range(30):
        if g_connect_status:
            print('connect network success')
            return True
        time.sleep(1)
    return False

def read_cfg_file():
    global t1,gas_threshold,ini_file_name
    try:
        f = open(ini_file_name,'r')
    except OSError:
        cfg_dict = {'gasstr':1.0,'t1':60}
        print('write',cfg_dict)
        f = open(ini_file_name,'w+')
        print(f)
        f.write(ujson.dumps(cfg_dict))
    else:
        cfg_txt = f.read()
        cfg_dict = ujson.loads(cfg_txt)
        if isinstance(cfg_dict,dict) == False:
            print('cfg_dict not a dict')
            return
        print('read',cfg_dict)
        gas_threshold = cfg_dict['gasstr']
        t1 = cfg_dict['t1']
        print('gas',gas_threshold,'t1',t1)
    finally:
        f.close()
        print('close')
    return 0


def write_cfg_file(cloudstr):
    global t1,gas_threshold,ini_file_name

    if isinstance(cloudstr,str) == False:
        return
    try:
        f = open(ini_file_name,'r')
    except OSError:
        pass
    else:
        cfg_txt = f.read()
        f.close()
    finally:
        pass

    try:
        f = open(ini_file_name,'w+')
    except OSError:
        pass
    else:
        cfg_dict = ujson.loads(cfg_txt)
        cloud_dict = ujson.loads(cloudstr)
        if isinstance(cfg_dict,dict) == False:
            print('cfg_dict not a dict')
            return

        if isinstance(cloud_dict,dict) == False:
            print('cloud_dict not a dict')
            return

        for key in cloud_dict.keys():
            if cfg_dict.get(key) != None:
                cfg_dict[key] = cloud_dict[key]
                if key == 'gasstr':
                    gas_threshold = cfg_dict[key]
                if key == 't1':
                    t1 = cfg_dict[key]

        f.seek(0)
        f.write(ujson.dumps(cfg_dict))
        print(cfg_dict)
        pass
    finally:
        f.close()
        print('cloud cfg file close')
    return

def on_connect():
    print('linkkit is connected')

def on_disconnect():
    print('linkkit is disconnected')

def on_props(request):
    print('clound req data is {}'.format(request))
    global gpio
    global cloud_ctrl

    cloudmsg = ujson.loads(request)
    if 'powerstate' in cloudmsg:
        if cloudmsg['powerstate'] == 0:
            gpio.write(0)
            #pass
            cloud_ctrl = 0
            print('led state {}'.format(cloudmsg['powerstate']))
        else:
            cloud_ctrl = 1
            gpio.write(1)
            #pass
            print('led state {}'.format(cloudmsg['powerstate']))
    else:
        write_cfg_file(request)

def on_service(id,request):
    print('clound req id  is {} , req is {}'.format(id,request))

def on_error(err):
    print('err msg is {} '.format(err))

def gas_detec():
    gas_val = 0.0
    dotnum = 0
    global uart1
    len1 = 0
    #sign = 0
    uart1.open('serial1')
    readbuf1 = bytearray(9)
    writebuf1 = bytearray([0xd7])
    readbuf2 = bytearray(13)
    writebuf2 = bytearray([0xff,0x01,0x87,0x00,0x00,0x00,0x00,0x00,0x78])

    uart1.write(writebuf1)
    len1 = uart1.read(readbuf1)
    print(readbuf1)
    if len1 != len(readbuf1):
        print('read dotnum err')
        uart1.close()
        return gas_val

    uart1.write(writebuf2)
    len1 = uart1.read(readbuf2)
    print(readbuf2)
    if len1 != len(readbuf2):
        print('read gas err')
        uart1.close()
        return gas_val

    uart1.close()
    dotnum = (readbuf1[6]&0xf0)>> 4
    #sign = readbuf1[6]&0x0f
    gas_val = (readbuf2[2]*256.000 + readbuf2[3])*1.000/10**dotnum
    print('gasvalue:',end='')
    print(gas_val)
    return gas_val

def liquid_level_detec():
    lowval = liq_level.read()
    print('lowval',lowval)
    liq_meicn_remain = False

    if lowval == 1:
        liq_meicn_remain = True
    else:
        liq_meicn_remain = False

    return liq_meicn_remain

def main():
    global liq_level,cloud_ctrl,t1,liq_mdcn_alarm,gas_alarm
    ret = connect_network()
    print('network register sta {}'.format(ret))

    productKey = 'xxxx'
    productSecret = ''
    deviceName = 'xxxx'
    deviceSecret = 'xxxxx'

    key_info = {
        'region' : 'cn-shanghai',
        'productKey' : productKey,
        'deviceName' : deviceName,
        'deviceSecret' : deviceSecret,
        'productSecret' : productSecret
    }

    device = iot.Device(key_info)

    device.on('connect',on_connect)
    device.on('disconnect',on_disconnect)
    device.on('props',on_props)
    device.on('service',on_service)
    device.on('error',on_error)

    device.connect()

    post_data = {'params':{'ver':version,'name':key_info['deviceName']}}
    device.postProps(post_data)

    read_cfg_file()

    time.sleep(2)
    led1 = GPIO()
    pump = GPIO()

    '''liqid level detec prompt led'''
    led1.open('led1')
    '''liquid level detec io'''
    liq_level.open('liq_level')

    '''control pump relay'''
    pump.open('pump')
    pump.write(1)
    '''cloud_flg is cloud down data led'''
    gpio.open('cloud_flg')
    time_cnt = 0
    gas_value = 0.00
    liq_mdcn_re_flg_chg = False
    need_send = False

    while True:
        time.sleep_ms(1000)
        time_cnt += 1
        liq_mdcn_re_flg = liquid_level_detec()
        if liq_mdcn_re_flg == False:
            led1.write(0)
            if liq_mdcn_re_flg_chg == True:
                liq_mdcn_re_flg_chg = False
                need_send = True
            pass
        else:
            led1.write(1)
            need_send = True
            liq_mdcn_re_flg_chg = True
            print('need send')
            '''need send data to cloud'''
            pass

        if time_cnt%10 == 0:
            gas_value = gas_detec()
            if gas_value > gas_threshold:
                '''need send data to cloud'''
                gas_alarm = True
                need_send = True
                print('need send')
            else:
                gas_alarm = False
            pass

        if liq_mdcn_re_flg == True:
            need_send = False
            pump.write(1)
            cloud_ctrl = 2
            print('close pump')
            post_data = {'params':{'liq_mdcn_re':0,'gasval':100,'gasalarm':0,'powerstate':0}}
            post_data['params']['liq_mdcn_re'] = 0
            gas_value = gas_detec()
            post_data['params']['gasval'] = int(gas_value*100)

            if gas_alarm == True:
                post_data['params']['gasalarm'] = 1

            post_data['params']['powerstate'] = gpio.read()

            device.postProps(post_data)
            continue
        if gas_alarm == False:
            if time_cnt%t1 == 0:
                if pump.read() == 1 :
                    pump.write(0)
                    print('open pump')
                else:
                    pump.write(1)
                    print('close pump')
            else:
                pass

            if cloud_ctrl == 0:
                pump.write(1)
                cloud_ctrl = 2
                time_cnt = 0
                print('cloud close pump')
            elif cloud_ctrl == 1:
                pump.write(0)
                cloud_ctrl = 2
                time_cnt = 0
                print('cloud open pump')
        elif gas_alarm == True:
            pump.write(1)
            print('gas alarm close pump')

        if need_send == True:
            need_send = False
            post_data1 = {'params':{'liq_mdcn_re':0,'gasval':100,'gasalarm':0,'powerstate':0}}
            if liq_mdcn_re_flg == True:
                post_data1['params']['liq_mdcn_re'] = 0
            else:
                post_data1['params']['liq_mdcn_re'] = 1

            post_data1['params']['gasval'] = int(gas_value*100)

            if gas_alarm == True:
                post_data1['params']['gasalarm'] = 1

            post_data['params']['powerstate'] = gpio.read()
            device.postProps(post_data1)


if __name__ == '__main__':
    main()

由于本案例中涉及到硬件端口的配置,因此需要修改默认的board.json .修改后的json 文件中内容如下:

{
    "version": "1.0.0",
    "io": {
        "ADC0": {
            "type": "ADC",
            "port": 0,
            "sampling": 12000000
        },
        "pca9544": {
        "type": "I2C",
        "port": 1,
        "addrWidth": 8,
        "freq": 100000,
        "mode": "master",
        "devAddr": 112
        },
        "qma8981": {
        "type": "I2C",
        "port": 1,
        "addrWidth": 7,
        "freq": 400000,
        "mode": "master",
        "devAddr": 18
        },
        "GPIO23": {
            "type": "GPIO",
            "port": 23,
            "dir": "irq",
            "pull": "pullup",
            "intMode": "rising"
        },
        "led1": {
            "type": "GPIO",
            "port": 0,
            "dir": "output",
            "pull": "pullup"
        },
        "pump": {
            "type": "GPIO",
            "port": 16,
            "dir": "output",
            "pull": "pullup"
        },
        "cloud_flg": {
            "type": "GPIO",
            "port": 7,
            "dir": "output",
            "pull": "pullup"
        },
        "liq_level": {
            "type": "GPIO",
            "port": 15,
            "dir": "input",
            "pull": "pullup"
        },
        "SPI0": {
        "type": "SPI",
        "port": 0,
        "mode": "master",
        "freq": 2000000
        },
    "serial1":{
        "type":"UART",
        "port":0,
        "dataWidth":8,
        "baudRate":9600,
        "stopBits":1,
        "flowControl":"disable",
        "parity":"none"
    },
    "serial2":{
        "type":"UART",
        "port":1,
        "dataWidth":8,
        "baudRate":115200,
        "stopBits":1,
        "flowControl":"disable",
        "parity":"none"
    },
    "serial3":{
        "type":"UART",
        "port":2,
        "dataWidth":8,
        "baudRate":115200,
        "stopBits":1,
        "flowControl":"disable",
        "parity":"none"
    }
    },
    "debugLevel": "DEBUG",
    "repl":"disable"
}

5. 阿里云物模型

以下是阿里云物模型的json配置文件

{
    "schema": "https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json",
    "profile": {
    "version": "1.0",
    "productKey": "a1iJzDtH0Hy"
    },
    "properties": [
    {
        "identifier": "ver",
        "name": "设备版本",
        "accessMode": "r",
        "required": false,
        "dataType": {
        "type": "text",
        "specs": {
            "length": "64"
        }
        }
    },
    {
        "identifier": "liq_mdcn_re",
        "name": "液位",
        "accessMode": "r",
        "required": false,
        "dataType": {
        "type": "bool",
        "specs": {
            "0": "低",
            "1": "高"
        }
        }
    },
    {
        "identifier": "gasval",
        "name": "气体值",
        "accessMode": "r",
        "required": false,
        "dataType": {
        "type": "int",
        "specs": {
            "min": "0",
            "max": "1000",
            "step": "1"
        }
        }
    },
    {
        "identifier": "gasalarm",
        "name": "气体告警状态",
        "accessMode": "r",
        "required": false,
        "dataType": {
        "type": "bool",
        "specs": {
            "0": "无告警",
            "1": "告警"
        }
        }
    },
    {
        "identifier": "powerstate",
        "name": "下行控制",
        "accessMode": "rw",
        "required": false,
        "dataType": {
        "type": "bool",
        "specs": {
            "0": "关",
            "1": "开"
        }
        }
    },
    {
        "identifier": "gasstr",
        "name": "气体浓度阈值",
        "accessMode": "rw",
        "required": false,
        "dataType": {
        "type": "float",
        "specs": {
            "min": "0.00",
            "max": "100.00",
            "unit": "mg/L",
            "unitName": "毫克每升",
            "step": "0.01"
        }
        }
    },
    {
        "identifier": "t1",
        "name": "t1",
        "accessMode": "rw",
        "required": false,
        "dataType": {
        "type": "int",
        "specs": {
            "min": "30",
            "max": "600",
            "step": "1"
        }
        }
    }
    ],
    "events": [
    {
        "identifier": "post",
        "name": "post",
        "type": "info",
        "required": true,
        "desc": "属性上报",
        "method": "thing.event.property.post",
        "outputData": [
        {
            "identifier": "ver",
            "name": "设备版本",
            "dataType": {
            "type": "text",
            "specs": {
                "length": "64"
            }
            }
        },
        {
            "identifier": "liq_mdcn_re",
            "name": "液位",
            "dataType": {
            "type": "bool",
            "specs": {
                "0": "低",
                "1": "高"
            }
            }
        },
        {
            "identifier": "gasval",
            "name": "气体值",
            "dataType": {
            "type": "int",
            "specs": {
                "min": "0",
                "max": "1000",
                "step": "1"
            }
            }
        },
        {
            "identifier": "gasalarm",
            "name": "气体告警状态",
            "dataType": {
            "type": "bool",
            "specs": {
                "0": "无告警",
                "1": "告警"
            }
            }
        },
        {
            "identifier": "powerstate",
            "name": "下行控制",
            "dataType": {
            "type": "bool",
            "specs": {
                "0": "关",
                "1": "开"
            }
            }
        },
        {
            "identifier": "gasstr",
            "name": "气体浓度阈值",
            "dataType": {
            "type": "float",
            "specs": {
                "min": "0.00",
                "max": "100.00",
                "unit": "mg/L",
                "unitName": "毫克每升",
                "step": "0.01"
            }
            }
        },
        {
            "identifier": "t1",
            "name": "t1",
            "dataType": {
            "type": "int",
            "specs": {
                "min": "30",
                "max": "600",
                "step": "1"
            }
            }
        }
        ]
    }
    ],
    "services": [
    {
        "identifier": "set",
        "name": "set",
        "required": true,
        "callType": "async",
        "desc": "属性设置",
        "method": "thing.service.property.set",
        "inputData": [
        {
            "identifier": "powerstate",
            "name": "下行控制",
            "dataType": {
            "type": "bool",
            "specs": {
                "0": "关",
                "1": "开"
            }
            }
        },
        {
            "identifier": "gasstr",
            "name": "气体浓度阈值",
            "dataType": {
            "type": "float",
            "specs": {
                "min": "0.00",
                "max": "100.00",
                "unit": "mg/L",
                "unitName": "毫克每升",
                "step": "0.01"
            }
            }
        },
        {
            "identifier": "t1",
            "name": "t1",
            "dataType": {
            "type": "int",
            "specs": {
                "min": "30",
                "max": "600",
                "step": "1"
            }
            }
        }
        ],
        "outputData": []
    },
    {
        "identifier": "get",
        "name": "get",
        "required": true,
        "callType": "async",
        "desc": "属性获取",
        "method": "thing.service.property.get",
        "inputData": [
        "ver",
        "liq_mdcn_re",
        "gasval",
        "gasalarm",
        "powerstate",
        "gasstr",
        "t1"
        ],
        "outputData": [
        {
            "identifier": "ver",
            "name": "设备版本",
            "dataType": {
            "type": "text",
            "specs": {
                "length": "64"
            }
            }
        },
        {
            "identifier": "liq_mdcn_re",
            "name": "液位",
            "dataType": {
            "type": "bool",
            "specs": {
                "0": "低",
                "1": "高"
            }
            }
        },
        {
            "identifier": "gasval",
            "name": "气体值",
            "dataType": {
            "type": "int",
            "specs": {
                "min": "0",
                "max": "1000",
                "step": "1"
            }
            }
        },
        {
            "identifier": "gasalarm",
            "name": "气体告警状态",
            "dataType": {
            "type": "bool",
            "specs": {
                "0": "无告警",
                "1": "告警"
            }
            }
        },
        {
            "identifier": "powerstate",
            "name": "下行控制",
            "dataType": {
            "type": "bool",
            "specs": {
                "0": "关",
                "1": "开"
            }
            }
        },
        {
            "identifier": "gasstr",
            "name": "气体浓度阈值",
            "dataType": {
            "type": "float",
            "specs": {
                "min": "0.00",
                "max": "100.00",
                "unit": "mg/L",
                "unitName": "毫克每升",
                "step": "0.01"
            }
            }
        },
        {
            "identifier": "t1",
            "name": "t1",
            "dataType": {
            "type": "int",
            "specs": {
                "min": "30",
                "max": "600",
                "step": "1"
            }
            }
        }
        ]
    }
    ]
}

6. 测试验证

6.2 创建IOT Studio移动端应用

6.2.1 创建阿里云物联网产品
  1. 在公共实例中,根据项目需求创建产品:

创建产品1

注意: 1. 在选择联网方式时,请选择蜂窝(2G / 3G / 4G / 5G)。

  1. 创建设备时建议使用模块镭雕上的15-17位IMEI号作为设备DeviceName,后期产品可以使用模块唯一识别号IMEI进行动态注册。

创建产品2
  1. 导入物模型数据

代码流程图 代码流程图

导入完成后如下所示:

代码流程图 代码流程图
  1. 添加设备

代码流程图 代码流程图

选择刚创建好的设备,点击查看设备物模型(此处为案例演示设备,显示的为测试数据,需自行上报数据)

代码流程图
6.2.2 创建移动端应用

A. 打开Iot Studio开发界面 产品和设备创建完成后,在控制台选择增值服务,进入Iot Studio开发界面。

代码流程图

B. 新建移动端应用 如下图所示,新建一个移动端的应用(此处的项目为事先创建好的,如有所需 也可以新建项目)。新建移动端应用。

代码流程图

C. 创建移动端应用 点击确定以后会自动进入创建好的移动端应用中,选择创建空白页面。

代码流程图

完成创建后进入正式页面编辑界面。

代码流程图

D. 选择组件 在组件中选择想要的基础组件,将拖拽至页面中,进行相应的布局设置。(本案例中只用到折线图和开关控制组件)

代码流程图

E. 组件配置 选择好对应的组件后,还需要对其进行数据源的配置。

  1. 点击组件,在右侧样式中选择配置数据源

代码流程图
  1. 数据源配置选择

代码流程图
  1. 选择关联产品,关联刚刚创建的产品HaaS506

代码流程图
  1. 选择关联设备

代码流程图 代码流程图

5. 控制开关组件数据源配置 返回移动端开发界面,刷新列表即可进入选择界面。

代码流程图

设置完成后,可以看到右侧样式栏显示开关数据已配置数据源,即为配置完成。 6. 折线图组件数据源配置 折线图组件需要创建一个SQL分析的API接口。

  1. Iot Studio控制台中选择数据任务,新建一个SQL分析,如下图所示:

23 24
  1. 进入数据分析界面,配置数据源。

25

如下图所示,选择设备名称和气体值,并点击生成SQL查询语句:

26
  1. SQL语句生成后,点击运行即可查询所选数据内容,分别点击配置、 测试以及发布。如下图所示:

27

点击配置后,设置对应的参数类型:

点击测试,测试数据是否正常,显示测试成功,即可点击发布。

29

发布后就可以返回移动端应用开发界面。

30
  1. 返回开发界面后点击折线图,在右侧样式中将刚发布的API接口作为折线图的数据源,如下图所示:

31

进入折线图配置界面,将所需维度拖拽至横纵轴,完成配置。

32
  1. 配置完成后,如下图所示,可以保存、预览以及发布

33
  1. 点击预览后会生成一个预览二维码,手机扫描二维码进行预览控制操作。

34

6.3 开发验证

准备硬件:
  1. 请参考上述准备 硬件环境。

  2. HaaS506 连接usb typeC 到pc,此时在pc端会生成8组usb serail port

  3. 其中port5即Unisoc Usb Serial Port 5对应的COM口为板子日志输出以及python命令行交互口

  4. HaaS506 使用HY转杜邦线4pin线材连接uart1 即板子丝印J8 TTL接口,杜邦线连接usb转uart模块,请参考上述接线方式。

软件准备:
  1. HaaS506最新固件,一般拿到开发板,厂家烧录最新的固件,无需重新烧录。下载地址以及烧录方式: HaaS 506

  2. VScode(大于1.57),HaaS studio VScode插件。

  3. 代码实现保存为main.py,board.json(HaaS506硬件驱动配置文件)

  4. 新建python轻应用工程`HaaS506IDE开发 <https://blog.csdn.net/HaaSTech/article/details/119379840>`_

  5. 将示例文件main.py以及硬件驱动配置文件board.json,一起放置到创建haas轻应用时的工作区路径下solution/test_demo(test_demo为项目名字)下,按照步骤4文档进行烧录验证。

  6. 扫描iot studio 预览二维码进行预览以及控制操作。