树莓派网关采集 RS-485 温湿度传感器以及 RS-485 门磁开关状态数据,上报到涂鸦云平台。同时,接收云端指令,树莓派网关处理之后,控制继电器动作,继电器返回当前的状态给云端。
树莓派有两个串口可以使用,一个是硬件串口(/dev/ttyAMA0),另一个是 mini 串口(/dev/ttyS0)。
在默认状态下,serial0(即 GPIO14,GPIO15)映射到 ttyS0(即 mini 串口:/dev/ttyS0)。ttyS0 的特点是其工作时钟来自于 CPU,CPU 的时钟是从 600MHZ 到 1.5Ghz 动态变化的,所以该串口经常会因为时钟频率变化而发生错误,因此本文没有用此串口和子设备通信。
serial1(跟板载蓝牙相连)映射到 ttyAMA0,ttyAMA0 是硬件串口,它的时钟频率不受 CPU 影响,因此本文树莓派和子设备通信采用了硬件串口(ttyAMA0)。通过配置交换映射关系,把硬件串口 ttyAMA0 映射到(GPIO14,GPIO15)上。
最终映射结果可以通过执行命令:ls -l /dev
来查看,发现串口 ttyAMA0 设备节点以及 ttyS0 设备节点已经交换。
DIR 管脚是控制 RS-485 转 TTL 模块发送与接收。当 DIR 高电平时为发送模式,当 DIR 为低电平时为接收模式。
树莓派可以连接路由器,也可以连接手机 Wi-Fi 热点,打开 SSH 服务。具体信息,可以参考下文所述登录树莓派的方法。
Modbus RTU 9600
使用树莓派配置的电源插座。
如果没有屏幕,就必须通过网络来登录和控制树莓派电脑,因此必须要有网络支持。可以通过手机开启 Wi-Fi 热点,修改树莓派的配置文件来连接手机网络,并且打开 SSH 服务(笔记本电脑也要连接手机的 Wi-Fi 热点)或者把树莓派通过网线连接到路由器,电脑连接路由器的无线网,使得树莓派和电脑在一个局域网内,并且打开 SSH 服务。下面分享两种基于 SSH 网络登录树莓派的方式。
如果没有路由器,或者已连接但无法知道树莓派的确切 IP 地址,该怎么办?可以通过电脑的双网卡进行分配。一般来说,现在的电脑都支持双网卡,一个有线网卡、一个无线网卡。可以直接用网线连接电脑网口和树莓派网口,配置电脑的无线接口共享给有线网卡,并对有线网卡进行网络共享。前提是需要设置有线网卡的 IPV4 为 DHCP 模式。具体步骤如下:
arp -a
,查看以 192.168.137.1
为网关的列表中出现的树莓派端口。通过插拔网线的方式,查看新出现的 IP 地址,即树莓派的 IP 地址。192.168.137.239
。需要在 涂鸦 IoT 开发平台 创建产品,获取授权信息,然后将产品和授权相关信息写入到代码中,接入涂鸦云。详细步骤如下:
登录 涂鸦 IoT 开发平台。
单击 创建产品 并 选择 行业解决方案 > 智慧工业 > 工业网关 品类。
单击 生态设备接入。
选择 TuyaLink 自定义方案。
输入产品名称,设备类型选择 网关设备,数据协议选择 涂鸦标准协议,通讯协议选择 以太网,并单击 创建产品。
在 功能定义 界面,单击 添加功能 并填写相关参数,完成产品功能定义。根据要实现的设备功能,创建 DP 功能点。
在 设备开发 界面,选择并下载 SDK 方案。
单击 下一步,进入激活信息获取页面。涂鸦提供免费的授权码供测试使用,您可以免费领取 2 个激活码。领取成功后,单击 注册设备,即完成设备注册。
设备相应信息会显示在下方。
将注册的设备信息,填写到 examples/data_model_basic_demo/data_model_basic_demo.c
文件中,编译并运行 Demo 即可连接涂鸦 IoT 云。关于编译的具体流程,请参考下文 编译执行 章节内容。
改之前:
const char productId[] = "3jbcpefnn1jxxxxx";
const char deviceId[] = "6ced2aa564727c01xxxxx";
const char deviceSecret[] = "ac5d367db39xxxxx";
改之后:
const char productId[] = "t1tlm6p13aoxxxxx";
const char deviceId[] = "6cf918e90b12f7b1fxxxxx";
const char deviceSecret[] = "a5f23a3fb34xxxxx";
注意:根据自己创建的网关产品 PID、设备 ID 和设备密钥进行更改。
子设备的创建流程同上,只有操作到下图的步骤时,按照下图圈出来的更改即可。
子设备创建完成后如下图,在网关设备创建的时候,DP 点已经创建,这里无需再次创建。
继电器:
温湿度传感器:
门磁传感器:
程序设计大致分为两部分,一部分是树莓派和 RS-485 子设备通信的程序,另外一部分是树莓派和涂鸦云平台进行交互的程序。
树莓派和涂鸦云平台进行数据交互的时候采用 C 语言编写。
在平台上创建网关设备时,下载 C TuyaLink SDK 开发包,在此 demo 上移植自己想要实现的功能。
在 data_model_basic_demo.c
实现整个控制逻辑:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "cJSON.h"
#include "tuya_cacert.h"
#include "tuya_log.h"
#include "tuya_error_code.h"
#include "system_interface.h"
#include "mqtt_client_interface.h"
#include "tuyalink_core.h"
const char productId[] = "t1tlm6p13aouh***";
const char deviceId[] = "6cf918e90b12f7b1ffw***";
const char deviceSecret[] = "a5f23a3fb341e***";
tuya_mqtt_context_t client_instance;
// 写文件
int write_file(char str[])
{
FILE *fp = NULL;
fp = fopen("/tmp/from_platform.txt", "w");
fprintf(fp, "%s", str);
fclose(fp);
printf("deviceid_action_time: %s\n", str);
return 0;
}
// 读文件
int read_file(int num,char return_data[])
{
FILE *fp = NULL;
switch (num)
{
case 1:
fp = fopen("/tmp/1.txt", "r");
if(fp == NULL)
{
printf("open /tmp/1.txt error!\n");
return 0;
}
fgets(return_data, 255, (FILE*)fp);
fclose(fp);
break;
case 2:
fp = fopen("/tmp/2.txt", "r");
if(fp == NULL)
{
printf("open /tmp/2.txt error!\n");
return 0;
}
fgets(return_data, 255, (FILE*)fp);
fclose(fp);
break;
case 3:
fp = fopen("/tmp/3.txt", "r");
if(fp == NULL)
{
printf("open /tmp/3.txt error!\n");
return 0;
}
fgets(return_data, 255, (FILE*)fp);
fclose(fp);
break;
default:
break;
}
printf("return_data:%s\n",return_data);
return 0;
}
// 写一个线程函数 void *函数名(void *arg)
void *thread_worker1(void *arg)
{
char json_temp_hum[255];
char json_door_state[255];
char json_data[255];
char json_data1[255];
while(1)
{
if(read_file(2,json_data) == 0)
{
sprintf(json_temp_hum,"%s",json_data);
printf("json_temp_hum:%s\r\n",json_temp_hum);
tuyalink_thing_property_report_with_ack(arg, NULL, json_temp_hum);
memset(json_data, 0, sizeof(json_data));
}
else
{
printf("read temp_hum error\r\n");
}
if(read_file(3,json_data1) == 0)
{
sprintf(json_door_state,"%s",json_data1);
printf("json_door_state:%s\r\n",json_door_state);
tuyalink_thing_event_trigger(arg, NULL, json_door_state);
memset(json_data1, 0, sizeof(json_data1));
}
else
{
printf("read door_state error\r\n");
}
sleep(2);
}
}
void on_connected(tuya_mqtt_context_t* context, void* user_data)
{
int error=0;
pthread_t t1;
tuyalink_subdevice_bind(context, "[{\"productId\":\"snigjkwkheaxueqa\",\"nodeId\":\"255\",\"clientId\":\"1\"}]");//继电器
tuyalink_subdevice_bind(context, "[{\"productId\":\"dtoqgbr5azgwvga3\",\"nodeId\":\"254\",\"clientId\":\"2\"}]");//门磁
tuyalink_subdevice_bind(context, "[{\"productId\":\"6jmmnuwavyxkcv1x\",\"nodeId\":\"1\",\"clientId\":\"3\"}]");//温湿度传感器
error=pthread_create(&t1,NULL,thread_worker1,context);
if(error)
{
printf("create pthread error!\n");
return;
}
}
void on_disconnect(tuya_mqtt_context_t* context, void* user_data)
{
TY_LOGI("on disconnect");
}
void on_messages(tuya_mqtt_context_t* context, void* user_data, const tuyalink_message_t* msg)
{
char json_relay_state[255];
TY_LOGI("on message id:%s, type:%d, code:%d", msg->msgid, msg->type, msg->code);
switch (msg->type) {
case THING_TYPE_MODEL_RSP:
TY_LOGI("Model data:%s", msg->data_string);
break;
case THING_TYPE_PROPERTY_SET:
TY_LOGI("property set:%s", msg->data_string);
break;
case THING_TYPE_PROPERTY_REPORT_RSP:
break;
case THING_TYPE_ACTION_EXECUTE:
TY_LOGI("action execute:%s", msg->data_string);
if(write_file(msg->data_string) == 0)
{
printf ("write ok\r\n");
printf ("data_string:%s\r\n",msg->data_string);
}
else
{
printf ("write error\r\n");
}
if(read_file(1,json_relay_state) == 0)
{
sprintf(msg->data_string,"%s",json_relay_state);
printf("json_relay_state:%s\r\n",json_relay_state);
tuyalink_thing_property_report(context, NULL, msg->data_string);
memset(json_relay_state, 0, sizeof(json_relay_state));
}
else
{
printf ("read relay_state error\r\n");
}
break;
default:
break;
}
printf("\r\n");
}
int main(int argc, char** argv)
{
int ret = OPRT_OK;
tuya_mqtt_context_t* client = &client_instance;
ret = tuya_mqtt_init(client, &(const tuya_mqtt_config_t) {
.host = "m2.tuyacn.com",
.port = 8883,
.cacert = tuya_cacert_pem,
.cacert_len = sizeof(tuya_cacert_pem),
.device_id = deviceId,
.device_secret = deviceSecret,
.keepalive = 60,
.timeout_ms = 2000,
.on_connected = on_connected,
.on_disconnect = on_disconnect,
.on_messages = on_messages
});
assert(ret == OPRT_OK);
ret = tuya_mqtt_connect(client);
assert(ret == OPRT_OK);
for (;;)
{
/* Loop to receive packets, and handles client keepalive */
tuya_mqtt_loop(client);
}
return ret;
}
注意:下文会提到树莓派网关、C 端和 Python 端等。具体含义如下:
树莓派做成网关,里面跑了两个进程,一个是采用 Python 语言处理子设备数据收发,以及 JSON 数据解析(Python 端)。另外一个进程是采用 C 语言处理,和涂鸦云平台进行交互(C 端)。
这些是在平台上创建网关设备时的参数,填入对应的位置即可。
const char productId[] = "t1tlm6p13aouh***";
const char deviceId[] = "6cf918e90b12f7b1ffw***";
const char deviceSecret[] = "a5f23a3fb341e***";
在 main 函数里,实例化和初始化一个设备对象 tuya_mqtt_context_t
,用来初始化产品 ID 和授权信息等配置参数以及循环接收数据包,并处理客户端保持连接。
int main(int argc, char** argv)
{
int ret = OPRT_OK;
tuya_mqtt_context_t* client = &client_instance;
ret = tuya_mqtt_init(client, &(const tuya_mqtt_config_t) {
.host = "m2.tuyacn.com",
.port = 8883,
.cacert = tuya_cacert_pem,
.cacert_len = sizeof(tuya_cacert_pem),
.device_id = deviceId,
.device_secret = deviceSecret,
.keepalive = 60,
.timeout_ms = 2000,
.on_connected = on_connected,
.on_disconnect = on_disconnect,
.on_messages = on_messages
});
assert(ret == OPRT_OK);
ret = tuya_mqtt_connect(client);
assert(ret == OPRT_OK);
for (;;)
{
/* Loop to receive packets, and handles client keepalive */
tuya_mqtt_loop(client);
}
return ret;
}
启动 TuyaOS SDK 服务。
ret = tuya_mqtt_connect(client);
//TuyaOS SDK 服务任务,数据接收处理,设备在线保活等任务处理
循环调用将当前线程产生给底层的 Link SDK 客户端。
tuya_mqtt_loop(client);
定义应用层事件回调,on_messages
回调函数用于应用层接收 SDK 事件通知,如数据功能点(DP)下发,云端连接状态通知。平台下发指令在此函数中实现。
void on_messages(tuya_mqtt_context_t* context, void* user_data, const tuyalink_message_t* msg)
{
char json_relay_state[255];
TY_LOGI("on message id:%s, type:%d, code:%d", msg->msgid, msg->type, msg->code);
switch (msg->type) {
case THING_TYPE_MODEL_RSP:
TY_LOGI("Model data:%s", msg->data_string);
break;
case THING_TYPE_PROPERTY_SET:
TY_LOGI("property set:%s", msg->data_string);
break;
case THING_TYPE_PROPERTY_REPORT_RSP:
break;
case THING_TYPE_ACTION_EXECUTE:
TY_LOGI("action execute:%s", msg->data_string);
if(write_file(msg->data_string) == 0)
{
printf ("write ok\r\n");
printf ("data_string:%s\r\n",msg->data_string);
}
else
{
printf ("write error\r\n");
}
if(read_file(1,json_relay_state) == 0)
{
sprintf(msg->data_string,"%s",json_relay_state);
printf("json_relay_state:%s\r\n",json_relay_state);
tuyalink_thing_property_report(context, NULL, msg->data_string);
memset(json_relay_state, 0, sizeof(json_relay_state));
}
else
{
printf ("read relay_state error\r\n");
}
break;
default:
break;
}
printf("\r\n");
}
THING_TYPE_ACTION_EXECUTE
主题中,树莓派网关从平台上获取的指令(JSON 格式)存入文件中,供 Python 端调用(Python 端处理后控制继电器动作)。树莓派网关同时从相应文件中读取设备数据值(Python 端获取设备值,处理成 JSON 格式存入文件中供 C 端调用),C 端处理成 JSON 格式的字符串上报到云端。
case THING_TYPE_ACTION_EXECUTE:
TY_LOGI("action execute:%s", msg->data_string);
if(write_file(msg->data_string) == 0)
{
printf ("write ok\r\n");
printf ("data_string:%s\r\n",msg->data_string);
}
else
{
printf ("write error\r\n");
}
if(read_file(1,json_relay_state) == 0)
{
sprintf(msg->data_string,"%s",json_relay_state);
printf("json_relay_state:%s\r\n",json_relay_state);
tuyalink_thing_property_report(context, NULL, msg->data_string);
memset(json_relay_state, 0, sizeof(json_relay_state));
}
else
{
printf ("read relay_state error\r\n");
}
break;
网关发现子设备,请求云端激活子设备并建立 topo 关系。适用于设备无法预先在云端注册,也无法烧录,网关发现子设备后,请求云端注册并绑定到当前网关下。在 MQTT 连接成功回调函数里面绑定了三个子设备。
productId
从平台上获取。nodeId
是设备的节点 ID。至少保证网关下唯一,可以是子设备的地址。clientId
是设备端唯一 ID,子设备硬件的唯一标识。可以是设备的 UUID、MAC、SN 等,至少保证产品下唯一。在 MQTT 连接成功回调函数中创建了一个子线程。
线程函数具体实现功能:用于不断获取温湿度数据以及门磁状态数据,同时上报到云平台。
//写一个线程函数 void *函数名(void *arg)
void *thread_worker1(void *arg)
{
char json_temp_hum[255];
char json_door_state[255];
char json_data[255];
char json_data1[255];
while(1)
{
if(read_file(2,json_data) == 0)
{
sprintf(json_temp_hum,"%s",json_data);
printf("json_temp_hum:%s\r\n",json_temp_hum);
tuyalink_thing_property_report_with_ack(arg, NULL, json_temp_hum);
memset(json_data, 0, sizeof(json_data));
}
else
{
printf("read temp_hum error\r\n");
}
if(read_file(3,json_data1) == 0)
{
sprintf(json_door_state,"%s",json_data1);
printf("json_door_state:%s\r\n",json_door_state);
tuyalink_thing_event_trigger(arg, NULL, json_door_state);
memset(json_data1, 0, sizeof(json_data1));
}
else
{
printf("read door_state error\r\n");
}
sleep(2);
}
}
下面是读文件函数,主要用于读取 Python 端获取子设备的数据值,然后放于数组 return_data
中。read_file
函数传入了两个参数,一个是 num,用于区分读取的文件(三个文件分别存入三个不同设备的数据值)。另外一个是数组 return_data
,用于存储读取文件的数据(供 C 端调用)。
//读文件
int read_file(int num,char return_data[])
{
FILE *fp = NULL;
switch (num)
{
case 1:
fp = fopen("/tmp/1.txt", "r");
if(fp == NULL)
{
printf("open /tmp/1.txt error!\n");
return 0;
}
fgets(return_data, 255, (FILE*)fp);
fclose(fp);
break;
case 2:
fp = fopen("/tmp/2.txt", "r");
if(fp == NULL)
{
printf("open /tmp/2.txt error!\n");
return 0;
}
fgets(return_data, 255, (FILE*)fp);
fclose(fp);
break;
case 3:
fp = fopen("/tmp/3.txt", "r");
if(fp == NULL)
{
printf("open /tmp/3.txt error!\n");
return 0;
}
fgets(return_data, 255, (FILE*)fp);
fclose(fp);
break;
default:
break;
}
printf("return_data:%s\n",return_data);
return 0;
}
下面是写文件函数,主要存储从云端获取的指令,然后供 Python 端调用。
//写文件
int write_file(char str[])
{
FILE *fp = NULL;
fp = fopen("/tmp/from_platform.txt", "w");
fprintf(fp, "%s", str);
fclose(fp);
printf("deviceid_action_time: %s\n", str);
return 0;
}
如果调试过程中报段错误,首先应该想到段错误的定义,从它出发考虑引发错误的原因。
在使用指针时,定义了指针后记得初始化指针,在使用的时候记得判断是否为 NULL。
在使用数组时,注意数组是否被初始化,数组下标是否越界,数组元素是否存在等。
在访问变量时,注意变量所占地址空间是否已经被程序释放掉。
在处理变量时,注意变量的格式控制是否合理等。
在调试的过程中,由于没有对打开文件为空时进行处理,导致出现了段错误,如下图所示,修改解决了此问题。
树莓派和 RS-485 子设备通信采用 Python 语言来编写。
在 new_temp_hum_door.py
文件中实现整个控制逻辑。
温湿度传感器和门磁状态获取函数里面引用类(从 new_relay_control
文件中引用 relay),执行相应的控制逻辑。While 循环读取文件中存储的数据(平台下发的指令,JSON 格式),指令解析之后,控制继电器动作以及循环获取温湿度传感器数据和门磁的状态,处理成 JSON 格式的字符串存入文件中(供 C 端读取)。
# -*- coding: utf-8 -*-
from new_relay_control import relay
from time import sleep
import json
#温湿度获取
def temp_hum_sensor_get():
temp_hum = relay()
temp_hum.all_relay = 3
temp_hum.relay_all_on_order = ['01 04 00 00 00 02 71 CB']
return_str = temp_hum.ALL_ON()
return return_str
#门磁状态获取
def door_sensor_get():
door_sensor = relay()
door_sensor.all_relay = 3
door_sensor.relay_all_on_order = ['FE 01 00 00 00 02 A9 C4']
return_str = door_sensor.ALL_ON()
return return_str
id_value = 2
time = 1
while True:
try:
#读文件(读平台下发的指令)
fp = open('/tmp/from_platform.txt', 'r')
str_read = fp.read()
# {"inputParams":{"relay_action":true},"actionCode":"relay"}
recv_json = json.loads(str_read)
relay_action = recv_json['inputParams']['action']
action_code = recv_json['actionCode']
print(relay_action)
print(action_code)
print("read from platform: %s " % str_read)
print("data type: %s" % (type(str_read)))
if action_code == "relay":
relay_open = relay()
if relay_action == True:
return_str = relay_open.ALL_ON()
else:
return_str = relay_open.ALL_OFF()
relay_str = return_str[8:12]
upload_to_platform = 0
print(relay_str)
if relay_str != "0000":
upload_to_platform = 1
string = "{\"actionCode\": \"relay\", \"actionTime\": 1626197189630,\"outputParams\": {\"relaystate\":0}}"
new_json = json.loads(string)
new_json['outputParams']['relaystate'] = upload_to_platform
final_str3 = json.dumps(new_json)
print(final_str3)
f = open('/tmp/1.txt', 'w')
f.write(final_str3)
f.close()
except FileNotFoundError:
print ("File is not found")
if id_value == 2:
return_str = temp_hum_sensor_get()
print(return_str)
get_str1 = return_str[6:10]
get_str2 = return_str[10:14]
try:
# temp_value = (int(get_str1, 16))/10
temp_value = int(get_str1, 16)
# hum_value = (int(get_str2, 16)) / 10
hum_value = int(get_str2, 16)
except ValueError:
pass
print(temp_value)
print(hum_value)
print(get_str1)
print(get_str2)
string1 = "{\"temp\":{\"value\":\"temp_value\",\"time\":1631708204231},\"hum\":{\"value\":\"hum_value\",\"time\":1631708204231}}"
new_json1 = json.loads(string1)
new_json1['temp']['value'] = temp_value
new_json1['hum']['value'] = hum_value
final_str4 = json.dumps(new_json1)
print(final_str4)
f = open('/tmp/2.txt', 'w')
f.write(final_str4)
f.close()
#if id_value == 3:
return_str = door_sensor_get()
get_str3 = return_str[6:8]
door_upload_to_platform = 0
if get_str3 == "00":
door_upload_to_platform = 0
else:
door_upload_to_platform = 1
string2 = "{\"eventCode\":\"door\",\"eventTime\":1626197189630,\"outputParams\":{\"doorsate\":0}}"
new_json2 = json.loads(string2)
new_json2['outputParams']['doorsate'] = door_upload_to_platform
final_str5 = json.dumps(new_json2)
print(get_str3)
print(final_str5)
f = open('/tmp/3.txt', 'w')
f.write(final_str5)
f.close()
sleep(time)
使用 class 定义类,实现继电器的控制逻辑。类里面定义了串口收发的函数 relay_send(self, send_order)
以及继电器打开与关闭的控制逻辑。
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import serial
from time import sleep
'''2路继电器开关控制函数,单独继电器开关控制和全部开关控制'''
class relay(object):
def __init__(self):
self.relay_all_on_order = ['02 05 00 00 FF 00 8C 09', '02 05 00 01 FF 00 DD C9', '02 0F 00 00 00 08 01 FF FE C0']
self.relay_all_off_order = ['02 05 00 00 00 00 CD F9', '02 05 00 01 00 00 9C 39', '02 0F 00 00 00 08 01 00 BE 80']
self.relay1 = 1
self.relay2 = 2
self.all_relay = 3
self.port = '/dev/ttyAMA0'
def relay_send(self, send_order):
if self.port:
relay_serial = serial.Serial(self.port, 9600)
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
if not relay_serial.isOpen():
relay_serial.Open()
while True:
GPIO.output(17, GPIO.HIGH)
sleep(0.01)
relay_serial.write(bytes.fromhex(send_order))
#relay_serial.write(bytes(send_order))
sleep(0.01)
GPIO.output(17, GPIO.LOW)
count = relay_serial.inWaiting()
if count > 0:
GPIO.output(17, GPIO.LOW)
sleep(0.01)
recv = relay_serial.read(count)
GPIO.output(17, GPIO.HIGH)
sleep(0.01)
print("recv: ", recv)
# recv_bytes = binascii.b2a_hex(recv)
# recv_str = binascii.b2a_hex(recv_bytes).decode('utf-8')
recv_str = str(recv.hex())
print("recv_str: ", recv_str)
print("recv_str: ", recv_str)
if recv_str == "00":
print("error")
else:
return recv_str
sleep(0.5)
#relay_serial.close()
def ALL_ON(self):
send_order = self.relay_all_on_order[self.all_relay - 3]
print(send_order)
get_return = self.relay_send(send_order)
print("继电器控制: ALL_RELAY_ON")
return get_return
def ALL_OFF(self):
send_order = self.relay_all_off_order[self.all_relay - 3]
get_return = self.relay_send(send_order)
print("继电器控制: ALL_RELAY_OFF")
return get_return
def RELAY1_ON(self):
send_order = self.relay_all_on_order[self.relay1 - 1]
get_return = self.relay_send(send_order)
print("继电器控制: RELAY1_ON")
return get_return
def RELAY1_OFF(self):
send_order = self.relay_all_off_order[self.relay1 - 1]
get_return = self.relay_send(send_order)
print("继电器控制: RELAY1_OFF")
return get_return
def RELAY2_ON(self):
send_order = self.relay_all_on_order[self.relay2 - 1]
get_return = self.relay_send(send_order)
print("继电器控制: RELAY2_ON")
return get_return
def RELAY2_OFF(self):
send_order = self.relay_all_off_order[self.relay2 - 1]
get_return = self.relay_send(send_order)
print("继电器控制: RELAY2_OFF")
return get_return
if __name__ == "__main__":
relay = relay()
relay.port = '/dev/ttyAMA0'
子设备通信程序需要在 Python 3 以上版本进行编译运行。如果 Python 是低版本,编译运行时会报错。
有些写法在低版本里是不支持的。例如下面写法,Python 2.7 版本会报错,Python 3 以上版本则不会。
temp_value = int(get_str1, 16)
hum_value = int(get_str2, 16)
下面编码声明在 Python 3 以上版本则不用,在低版本需要声明,不然运行会报错。
# -*- coding: utf-8 -*-
下面写法同样在 Python 3 以上版本适用,在低版本中则不支持,不然运行会报错。
recv_str = str(recv.hex())
若出现下面读文件错误,导致编译运行中断,则是由于当文件不存在时,没有进行异常处理。由于收到平台指令才开始创建文件,所以没有指令下发时文件没有创建,导致运行 Python 端程序时会报错。
可以参照下图进行解决:(添加捕获异常处理)
安装 make
等相关环境依赖。
sudo apt-get install make cmake
新建一个文件夹开始编译。
mkdir build && cd build
cmake ..
make
运行 Demo。
./bin/data_model_basic_demo
在设备端查看运行接口。以下日志显示设备与涂鸦云平台连接成功。
设备成功连接到涂鸦云平台后,单击进行刷新,设备状态会显示为在线。
在平台上,查看上报的消息以及对设备下发相应指令。
温湿度值上报云平台(属性)。
数据有没有上报成功,看 code 值:
门磁设备上报数据到云平台(事件)。
数据有没有上报成功,看 code 值:
平台下发指令,继电器动作(动作)。
树莓派的引脚短路,特别是 VCC 和 GND,短路会造成芯片烧毁无法恢复。
树莓派启动需要几十秒时间,打开电源后 1 分钟内不可以关闭电源,会影响树莓派的使用寿命。
SD 卡烧录系统完成时,系统会提示格式化,此时不需要格式化,单击取消即可。若点了格式化后,树莓派会提示缺失文件,需要重新烧录系统。
树莓派 4B 的 HDMI 接口变成两个 micro-HDMI 接口(hdmi0 和 hdmi1),可以接入两个显示器。如果只连接一个显示器,一定要插入 hdmi0 接口,也就是靠近 type-C 电源接口的那一个,才可以正常显示。如果只插入 hdmi1 接口,会出现显示器无法显示的情况。
- 注意
HDMI 线反接会导致接口损坏!
拆插设备前,需要先断电。
由于树莓派跑了两个进程,因此整个过程中需要两个程序同时运行。
软件调试过程中的其它注意事项已在 程序设计 中具体指出。
该内容对您有帮助吗?
是我要提建议