【OrangePi Zero2 智能家居】智能家居项目的软件实现

news2024/11/26 8:53:15

一、项目整体设计
二、项目代码的前期准备
三、实现语音监听接口
四、实现socket监听接口
五、实现烟雾报警监听接口
六、实现设备节点代码
七、实现接收消息处理接口

一、项目整体设计

整体的软件框架大致如下:
在这里插入图片描述
整个项目开启4个监听线程, 分别是:

  1. 语音监听线程:用于监听语音指令, 当有语音指令过来后, 通过消息队列的方式给消息处理线程发
    送指令
  2. 网络监听线程:用于监听网络指令,当有网络指令过来后, 通过消息队列的方式给消息处理线程发
    送指令
  3. 火灾检测线程:当存在煤气泄漏或者火灾闲情时, 发送警报指令给消息处理线程
  4. 消息监听线程: 用于处理以上3个线程发过来的指令,并根据指令要求配置GPIO引脚状态,OLED
    屏显示、语音播报,还有人脸识别开门
    上述四个线程采用统一个对外接口接口,同时添加到监听链表中。

统一的监听模块接口如下:

struct control
{
	char control_name[128]; //监听模块名称
	int (*init)(void); //初始化函数
	void (*final)(void);//结束释放函数
	void *(*get)(void *arg);//监听函数,如语音监听
	void *(*set)(void *arg); //设置函数,如语音播报
	struct control *next;
};

struct control *add_device_to_ctrl_list(struct control *phead, struct control *device);

另外,被控制的设备类也统一配置接口,同时添加到设备链表中。

统一的设备类接口如下:

struct gdevice
{
	char dev_name[128]; //设备名称
	int key; //key值,用于匹配控制指令的值
	int gpio_pin; //控制的gpio引脚
	int gpio_mode; //输入输出模式
	int gpio_status; //高低电平状态
	int check_face_status; //是否进行人脸检测状态
	int voice_set_status; //是否语音语音播报
	struct gdevice *next;
};

struct gdevice *add_device_to_gdevice_list(struct gdevice *phead, struct gdevice *device);
struct gdevice *find_gdevice_by_key(struct gdevice *pdev, unsigned char key);
int set_gpio_gdevice_status(struct gdevice *pdev);

二、项目代码的前期准备

之前讲过智能分类的项目,因为会用到语音模块、OLED显示、网络模块、这些代码都可以从智能分类的项目中直接拷贝过来使用,另外添加之前准备好的人脸识别的代码 。 另外根据《项目整体设计》。再定义gdevice.h和control.h的头文件。整个目录结构如下:

pg@pg-Default-string:~/smarthome$ tree -I 3rd/ #3rd目录直接从garbage工程拷贝过来, 主要是一些依赖库和头文件, 这里就不显示
.
├── inc
│ ├── control.h
│ ├── face.h
│ ├── gdevice.h
│ ├── myoled.h
│ ├── socket.h
│ └── uartTool.h
├── Makefile
└── src
	├── face.c
	├── face.py
	├── myoled.c
	├── socket.c
	└── uartTool.c

其中 control.h代码如下:

#ifndef __CONTROL__H
#define __CONTROL__H
#include <stdlib.h>

struct control
{
	char control_name[128];
	int (*init)(void);
	void (*final)(void);
	void *(*get)(void *arg);
	void *(*set)(void *arg);
	struct control *next;
};

//头插法,用于control类链表的创建
struct control *add_device_to_ctrl_list(struct control *phead, struct control *device);
#endif

// /dev/ttyS5 115200 ip port buffer pin /dev/I2C-3

control.c 代码如下:

#include "control.h"
//头插法
struct control *add_device_to_ctrl_list(struct control *phead, struct control *device)
{
	struct control *pcontrol;
	
	if(NULL == phead){
		pcontrol = device;
		return pcontrol;
	}else{
		device->next = phead;
		phead = device;
		return phead;
	}
}

gdevice.h 代码如下:

#ifndef __GDEVICE_H
#define __GDEVICE_H

struct gdevice
{
	char dev_name[128]; //设备名称
	int key; //key值,用于匹配控制指令的值
	int gpio_pin; //控制的gpio引脚
	int gpio_mode; //输入输出模式
	int gpio_status; //高低电平状态
	int check_face_status; //是否进行人脸检测状态
	int voice_set_status; //是否语音语音播报
	struct gdevice *next;
};
#endif

gdevice.c 代码如下:

#include <wiringPi.h>
#include "gdevice.h"

//根据key值(buffer[2])查找设备节点
struct gdevice *find_gdevice_by_key(struct gdevice *pdev, unsigned char key)
{
	struct gdevice *p = NULL;
	
	if (NULL == pdev)
	{
		return NULL;
	}
	
	p = pdev;
	
	while (NULL != p)
	{
		if(p->key == key)
		{
			return p;
		}
		p = p->next;
	}
	return NULL;
}

//设置GPIO引脚状态,输入输出和高低电平
int set_gpio_gdevice_status(struct gdevice *pdev)
{
	if (NULL == pdev)
	{
		return -1;
	}
	
	if (-1 != pdev->gpio_pin)
	{
		if (-1 != pdev->gpio_mode)
		{
			pinMode(pdev->gpio_pin, pdev->gpio_mode);
		}
		
		if (-1 != pdev->gpio_status)
		{
			digitalWrite(pdev->gpio_pin, pdev->gpio_status);
		}
	}
	return 0;
}

//链表头插法
struct gdevice *add_device_to_gdevice_list(struct gdevice *phead, struct gdevice *device)
{
	struct gdevice *pgdevice;
	
	if(NULL == phead){
		pgdevice = device;
		return pgdevice;
	}else{
		device->next = phead;
		phead = device;
		return phead;
	}
}

Makefile 修改后内容如下:

CC := aarch64-linux-gnu-gcc
SRC := $(shell find src -name "*.c")
INC := ./inc \
		./3rd/usr/local/include \
		./3rd/usr/include \
		./3rd/usr/include/python3.10 \
		./3rd/usr/include/aarch64-linux-gnu/python3.10 \
		./3rd/usr/include/aarch64-linux-gnu
		
OBJ := $(subst src/,obj/,$(SRC:.c=.o))

TARGET=obj/smarthome

CFLAGS := $(foreach item, $(INC),-I$(item)) # -I./inc -I./3rd/usr/local/include
LIBS_PATH := ./3rd/usr/local/lib \
			./3rd/lib/aarch64-linux-gnu \
			./3rd/usr/lib/aarch64-linux-gnu \
			./3rd/usr/lib/python3.10 \
							#L
							
LDFLAGS := $(foreach item, $(LIBS_PATH),-L$(item)) # -L./3rd/usr/local/libs
LIBS := -lwiringPi -lpython3.10 -pthread -lexpat -lz -lcrypt
obj/%.o:src/%.c
	mkdir -p obj
	$(CC) -o $@ -c $< $(CFLAGS)
	
$(TARGET) :$(OBJ)
	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)
	
compile : $(TARGET)

clean:
	rm $(TARGET) obj $(OBJ) -rf
	
debug:
	echo $(CC)
	echo $(SRC)
	echo $(INC)
	echo $(OBJ)
	echo $(TARGET)
	echo $(CFLAGS)
	echo $(LDFLAGS)
	echo $(LIBS)
	
.PHONY: clean compile debug

三、实现语音监听接口

语音监听模块会借助消息队列进行消息的传递,因此先实现消息队列的接口 msg_queque.c:

#include <stdio.h>
#include "msg_queue.h"

#define QUEQUE_NAME "/mq_queue"

mqd_t msg_queue_create(void)
{
	//创建消息队列
	mqd_t mqd = -1;
	struct mq_attr attr;
	attr.mq_flags = 0;
	attr.mq_maxmsg = 10;
	attr.mq_msgsize = 256;
	attr.mq_curmsgs = 0;
	
	mqd = mq_open(QUEQUE_NAME, O_CREAT | O_RDWR, 0666, &attr);
	printf("%s| %s |%d: mqd = %d\n",__FILE__, __func__, __LINE__, mqd);
	
	return mqd;
}

void msg_queue_final(mqd_t mqd)
{
	if (-1 != mqd){
		mq_close(mqd);
		mq_unlink(QUEQUE_NAME);
		mqd = -1;
	}
}

int send_message(mqd_t mqd, void *msg, int msg_len)
{
	int byte_send = -1;
	
	byte_send = mq_send(mqd, (char *)msg, msg_len, 0);
	
	return byte_send;
}

msg_queue.h 头文件定义:

#ifndef __MSG_QUEQUE_H
#define __MSG_QUEQUE_H

#include <mqueue.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

mqd_t msg_queue_create(void);
void msg_queue_final(mqd_t mqd);
int send_message(mqd_t mqd, void *msg, int msg_len);

#endif

根据control.h头文件的定义,实现语音监听接口

首先定义全局变量用于mqd句柄和struct control链表的传递 global.h 代码

#ifndef __GLOBAL__H
#define __GLOBAL__H

typedef struct {
	mqd_t mqd;
	struct control *ctrl_phead;
}ctrl_info_t;
#endif

紧接着语音监听接口 voice_interface.c 代码

#if 0
struct control
{
	char control_name[128]; //监听模块名称
	int (*init)(void); //初始化函数
	void (*final)(void);//结束释放函数
	void *(*get)(void *arg);//监听函数,如语音监听
	void *(*set)(void *arg); //设置函数,如语音播报
	struct control *next;
};
#endif
#include <pthread.h>
#include <stdio.h>

#include "voice_interface.h"
#include "uartTool.h"
#include "msg_queue.h"
#include "global.h"

static int serial_fd = -1;

static int voice_init(void)
{
	serial_fd = myserialOpen (SERIAL_DEV, BAUD);
	printf("%s|%s|%d:serial_fd=%d\n", __FILE__, __func__, __LINE__, serial_fd);
	
	return serial_fd;
}

static void voice_final(void)
{
	if (-1 != serial_fd)
	{
		close(serial_fd);
		serial_fd = -1;
	}
}

//接收语音指令
static void *voice_get(void *arg) // mqd应该来自于arg传参
{
	unsigned char buffer[6] = {0x00, 0x00, 0x00, 0x00, 0X00, 0x00};
	int len = 0;
	mqd_t mqd = -1;
	ctrl_info_t *ctrl_info= NULL;
	if (NULL != arg)
		ctrl_info = (ctrl_info_t *)arg;
		
	if (-1 == serial_fd)
	{
		serial_fd = voice_init();
		if (-1 == serial_fd)
		{
			pthread_exit(0);
		}
	}
	
	if(NULL != ctrl_info)
	{
		mqd = ctrl_info->mqd;
	}
	
	if ((mqd_t)-1 == mqd)
	{
		pthread_exit(0);
	}
	pthread_detach(pthread_self());
		
	printf("%s thread start\n", __func__);
	
	while(1)
	{
		len = serialGetstring(serial_fd, buffer);
		printf("%s|%s|%d:0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n", __FILE__, __func__, __LINE__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],buffer[5]);
		printf("%s|%s|%d:len=%d\n", __FILE__, __func__, __LINE__, len);
		
		if (len > 0)
		{
			if(buffer[0] == 0xAA && buffer[1] == 0x55
			&& buffer[5] == 0xAA && buffer[4] == 0x55)
			{
				printf("%s|%s|%d:send 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n", __FILE__, __func__, __LINE__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],buffer[5]);
				send_message(mqd, buffer, len);//注意,不要用strlen去计算实际的长度
			}
			memset(buffer, 0, sizeof(buffer));
		}
	}
	pthread_exit(0);
}

//语音播报
static void *voice_set(void *arg)
{
	pthread_detach(pthread_self());
	unsigned char *buffer = (unsigned char *)arg;
	
	if (-1 == serial_fd)
	{
		serial_fd = voice_init();
		if (-1 == serial_fd)
		{
			pthread_exit(0);
		}
	}
	
	if (NULL != buffer)
	{
		serialSendstring(serial_fd, buffer, 6);
	}
	pthread_exit(0);
}

struct control voice_control = {
	.control_name = "voice",
	.init = voice_init,
	.final = voice_final,
	.get = voice_get,
	.set = voice_set,
	.next = NULL
};

struct control *add_voice_to_ctrl_list(struct control *phead)
{//头插法
	return add_interface_to_ctrl_list(phead, &voice_control);
};

voice_interface.h代码

#ifndef ___VOICE_INTERFACE_H___
#define ___VOICE_INTERFACE_H___

#include "control.h"

struct control *add_voice_to_ctrl_list(struct control *phead);

#endif

四、实现socket监听接口

参考voice接口实现socket 接口socket_interface.c代码

#include <pthread.h>

#include "socket.h"
#include "control.h"
#include "socket_interface.h"
#include "msg_queue.h"
#include "global.h"

static int s_fd = -1;

static int tcpsocket_init(void)
{
	s_fd = socket_init(IPADDR, IPPORT);
	return -1;
}

static void tcpsocket_final(void)
{
	close(s_fd);
	s_fd = -1;
}

static void* tcpsocket_get(void *arg)
{
	int c_fd = -1;
	int ret = -1;
	struct sockaddr_in c_addr;
	unsigned char buffer[BUF_SIZE];
	mqd_t mqd = -1;
	ctrl_info_t *ctrl_info= NULL;
	int keepalive = 1; // 开启TCP_KEEPALIVE选项
	int keepidle = 10; // 设置探测时间间隔为10秒
	int keepinterval = 5; // 设置探测包发送间隔为5秒
	int keepcount = 3; // 设置探测包发送次数为3次
	
	pthread_detach(pthread_self());
	printf("%s|%s|%d: s_fd = %d\n", __FILE__, __func__, __LINE__,s_fd);
	
	if (-1 == s_fd)
	{
		s_fd = tcpsocket_init();
		if (-1 == s_fd)
		{
			printf("tcpsocket_init failed\n");
			pthread_exit(0);
		}
	}
	if (NULL != arg)
		ctrl_info = (ctrl_info_t *)arg;
		
	if(NULL != ctrl_info)
	{
		mqd = ctrl_info->mqd;
	}
	if ((mqd_t)-1 == mqd)
	{
		pthread_exit(0);
	}
	memset(&c_addr,0,sizeof(struct sockaddr_in));
	
	//4. accept
	int clen = sizeof(struct sockaddr_in);
	printf("%s thread start\n", __func__);
	while (1)
	{
		c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
		if (-1 == c_fd)
		{
			continue;
		}
		
		ret = setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)); // 设置TCP_KEEPALIVE选项
		if (ret == -1) { // 如果设置失败,打印错误信息并跳出循环
			perror("setsockopt");
			break;
		}
		
		ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle)); // 设置探测时间间隔选项
		if (ret == -1) { // 如果设置失败,打印错误信息并跳出循环
			perror("setsockopt");
			break;
		}
		
		ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval,
		sizeof(keepinterval)); // 设置探测包发送间隔选项
		if (ret == -1) { // 如果设置失败,打印错误信息并跳出循环
			perror("setsockopt");
			break;
		}
		
		ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount,
		sizeof(keepcount)); // 设置探测包发送次数选项
		if (ret == -1) { // 如果设置失败,打印错误信息并跳出循环
			perror("setsockopt");
			break;
		}
		
		printf("Accepted a connection from %s:%d\n", inet_ntoa(c_addr.sin_addr),
		ntohs(c_addr.sin_port)); // 打印客户端的IP地址和端口号
		
		while (1)
		{
			memset(buffer, 0, BUF_SIZE);
			ret = recv(c_fd, buffer, BUF_SIZE, 0);
			printf("%s|%s|%d: 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n", __FILE__, __func__, __LINE__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],buffer[5]);
			if (ret > 0)
			{
				if(buffer[0] == 0xAA && buffer[1] == 0x55
				&& buffer[5] == 0xAA && buffer[4] == 0x55)
				{
					printf("%s|%s|%d:send 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n", __FILE__, __func__, __LINE__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],buffer[5]);
					send_message(mqd, buffer, ret);//注意,不要用strlen去计算实际的长度
				}
			}
			else if ( -1 == ret || 0 == ret)
			{
				break;
			}
		}
	}
	pthread_exit(0);
}

struct control tcpsocket_control = {
	.control_name = "tcpsocket",
	.init = tcpsocket_init,
	.final = tcpsocket_final,
	.get = tcpsocket_get,
	.set = NULL,
	.next = NULL
};

struct control *add_tcpsocket_to_ctrl_list(struct control *phead)
{//头插法
	return add_interface_to_ctrl_list(phead, &tcpsocket_control);
};

socket.h 代码

#ifndef ___SOCKET_INTERFACE_H___
#define ___SOCKET_INTERFACE_H___

#include "control.h"

struct control *add_tcpsocket_to_ctrl_list(struct control *phead);

#endif

五、实现烟雾报警监听接口

同样参考voice接口实现smoke 接口smoke_interface.c代码

#include <pthread.h>
#include <wiringPi.h>
#include <stdio.h>

#include "control.h"
#include "smoke_interface.h"
#include "msg_queue.h"
#include "global.h"

#define SMOKE_PIN 6
#define SMOKE_MODE INPUT

static int smoke_init(void)
{
	printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
	pinMode(SMOKE_PIN, SMOKE_MODE);
	return 0;
}

static void smoke_final(void)
{
	//do nothing;
}

static void* smoke_get(void *arg)
{
	int status = HIGH;
	int switch_status = 0;
	unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA};
	ssize_t byte_send = -1;
	mqd_t mqd = -1;
	ctrl_info_t *ctrl_info = NULL;
	
	if (NULL != arg)
		ctrl_info = (ctrl_info_t *)arg;
		
	if(NULL != ctrl_info)
	{
		mqd = ctrl_info->mqd;
	}
	
	if ((mqd_t)-1 == mqd)
	{
		pthread_exit(0);
	}
	pthread_detach(pthread_self());
	printf("%s thread start\n", __func__);
	
	while(1)
	{
		status = digitalRead(SMOKE_PIN);
		if (LOW == status)
		{
			buffer[2] = 0x45;
			buffer[3] = 0x00;
			switch_status = 1;
			printf("%s|%s|%d:send 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n", __FILE__, __func__, __LINE__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],buffer[5]);
			byte_send = mq_send(mqd, buffer, 6, 0);
			if (-1 == byte_send)
			{
				continue;
			}
		}
		else if (HIGH == status && 1 == switch_status)
		{
			buffer[2] = 0x45;
			buffer[3] = 0x01;
			switch_status = 0;
			printf("%s|%s|%d:send 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n", __FILE__, __func__, __LINE__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],buffer[5]);
			byte_send = mq_send(mqd, buffer, 6, 0);
			
			if (-1 == byte_send)
			{
				continue;
			}
		}
		sleep(5);
	}
	pthread_exit(0);
}

struct control smoke_control = {
	.control_name = "smoke",
	.init = smoke_init,
	.final = smoke_final,
	.get = smoke_get,
	.set = NULL,
	.next = NULL
};

struct control *add_smoke_to_ctrl_list(struct control *phead)
{//头插法
	return add_interface_to_ctrl_list(phead, &smoke_control);
};

smoke_interface.h 代码

#ifndef ___SMOKE_INTERFACE_H___
#define ___SMOKE_INTERFACE_H___

#include "control.h"

struct control *add_smoke_to_ctrl_list(struct control *phead);

#endif

六、实现设备节点代码

  1. 客厅灯设备节点
    由于消息接收处理线程需要处理各设备类外设,因此先根据gdevice.h定义,实现客厅灯设备节点代码
    lrled_gdevice.c:
#include "gdevice.h"

struct gdevice lrled_gdev = {
	.dev_name = "LV led",
	.key = 0x41,
	.gpio_pin = 2,
	.gpio_mode = OUTPUT,
	.gpio_status = HIGH,
	.check_face_status = 0,
	.voice_set_status = 0,
};

struct gdevice *add_lrled_to_gdevice_list(struct gdevice *pgdevhead)
{//头插法
	return add_device_to_gdevice_list(pgdevhead, &lrled_gdev);
};

lrled_gdevice.h 代码如下:

#ifndef __LRLED_GDEVICE_H
#define __LRLED_GDEVICE_H

struct gdevice *add_lrled_to_gdevice_list(struct gdevice *pgdevhead);

#endif
  1. 卧室灯设备节点
    卧室灯设备节点代码bled_gdevice.c:
#include "gdevice.h"

struct gdevice bled_gdev = {
	.dev_name = "BR led",
	.key = 0x42,
	.gpio_pin = 5,
	.gpio_mode = OUTPUT,
	.gpio_status = HIGH,
	.check_face_status = 0,
	.voice_set_status = 0,
};

struct gdevice *add_bled_to_gdevice_list(struct gdevice *pgdevhead)
{//头插法
	return add_device_to_gdevice_list(pgdevhead, &bled_gdev);
};

bled_gdevice.h 代码如下:

#ifndef __BLED_GDEVICE_H
#define __BLED_GDEVICE_H

struct gdevice *add_bled_to_gdevice_list(struct gdevice *pgdevhead);

#endif
  1. 实现风扇设备节点代码
    实现风扇设备节点代码fan_gdevice.c:
#include "gdevice.h"

struct gdevice gdevice_fan = {
	.dev_name = "fan",
	.key = 0x43,
	.gpio_pin = 7,
	.gpio_mode = OUTPUT,
	.gpio_status = LOW,
	.check_face_status = 0,
	.voice_set_status = 0,
	.next = NULL
};

struct gdevice *add_fan_to_gdevice_list(struct gdevice *phead)
{
	return add_device_to_gdevice_list(phead, &gdevice_fan);
}

fan_gdevice.h 代码如下:

#ifndef __FAN_GDEVICE_H
#define __FAN_GDEVICE_H

struct gdevice *add_fan_to_gdevice_list(struct gdevice *pgdevhead);

#endif
  1. 蜂鸣器设备节点
    蜂鸣器设备节点代码beep_gdevice.c:
#include "gdevice.h"

struct gdevice beep_gdev = {
	.dev_name = "beep",
	.key = 0x45,
	.gpio_pin = 9,
	.gpio_mode = OUTPUT,
	.gpio_status = HIGH,
	.check_face_status = 0,
	.voice_set_status = 1,
};

struct gdevice *add_beep_to_gdevice_list(struct gdevice *pgdevhead)
{//头插法
	return add_device_to_gdevice_list(pgdevhead, &beep_gdev);
};

beep_gdevice.h 代码如下:

#ifndef __BEEP_GDEVICE_H
#define __BEEP_GDEVICE_H

struct gdevice *add_beep_to_gdevice_list(struct gdevice *pgdevhead);

#endif

七、实现接收消息处理接口

同样参考voice接口实现receive 接口receive_interface.c代码

#include "gdevice.h"

struct gdevice lock_gdev = {
	.dev_name = "lock",
	.key = 0x44,
	.gpio_pin = 8,
	.gpio_mode = OUTPUT,
	.gpio_status = HIGH,
	.check_face_status = 1,
	.voice_set_status = 1,
};
struct gdevice *add_lock_to_gdevice_list(struct gdevice *pgdevhead)
{//头插法
	return add_device_to_gdevice_list(pgdevhead, &lock_gdev);
};

receive.h 头文件代码

#ifndef ___RECEIVE_INTERFACE_H___
#define ___RECEIVE_INTERFACE_H___

#include "control.h"

struct control *add_receive_to_ctrl_list(struct control *phead);

#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1449122.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java常用类与基础API--String的构造器与常用方法

文章目录 一、String的常用API-1&#xff08;1&#xff09;构造器1、介绍2、举例 &#xff08;2&#xff09;String与其他结构间的转换1、基本数据类型、包装类 --> 字符串2、字符串 --> 基本数据类型、包装类3、字符串 --> 字符数组4、字符数组 --> 字符串5、字符…

C++类和对象-C++运算符重载->加号运算符重载、左移运算符重载、递增运算符重载、赋值运算符重载、关系运算符重载、函数调用运算符重载

#include<iostream> using namespace std; //加号运算符重载 class Person { public: Person() {}; Person(int a, int b) { this->m_A a; this->m_B b; } //1.成员函数实现 号运算符重载 Person operator(const Per…

4核16G服务器价格腾讯云PK阿里云

4核16G服务器租用优惠价格26元1个月&#xff0c;腾讯云轻量4核16G12M服务器32元1个月、96元3个月、156元6个月、312元一年&#xff0c;阿腾云atengyun.com分享4核16服务器租用费用价格表&#xff0c;阿里云和腾讯云详细配置报价和性能参数表&#xff1a; 腾讯云4核16G服务器价…

JavaWeb学习|Filter与ThreadLocal

学习材料声明 所有知识点都来自互联网&#xff0c;进行总结和梳理&#xff0c;侵权必删。 引用来源&#xff1a;尚硅谷最新版JavaWeb全套教程,java web零基础入门完整版 Filter 1、Filter 过滤器它是 JavaWeb 的三大组件之一。三大组件分别是&#xff1a;Servlet 程序、Liste…

Oracle数据库自动维护任务(Automated Maintenance Tasks)

Oracle数据库自动维护任务(Automated Maintenance Tasks) Oracle数据库有以下预定义的自动维护任务: Automatic Optimizer Statistics Collection - 收集数据库中没有统计信息或只有过时统计信息的所有模式对象的优化器统计信息。SQL查询优化器使用该任务收集的统计信息来提高…

数学实验第三版(主编:李继成 赵小艳)课后练习答案(十)(2)(3)

实验十&#xff1a;非线性函数极值求解 练习二 1.求解极值问题: (1) s.t. function [c,ceq]fun(x) c(1)-(25-x(1)^2-x(2)^2); c(2)-(7-x(1)^2x(2)^2); ceq0;换一个窗口运行下面的程序&#xff1a; clc;clear; f(x)-2*x(1)-x(2); a[]; b[]; aeq[];beq[]; u[5;10]; l[0;0];…

一起玩儿Proteus仿真(C51)——06. 红绿灯仿真(二)

摘要&#xff1a;本文介绍如何仿真红绿灯 今天来看一下红绿灯仿真程序的具体实现方法。先来看一下整个程序的原理图。 在这个红绿灯仿真实验中&#xff0c;每个路口需要控制的设备是2位数码管显示倒计时以及红黄绿灯的亮灭。先来看一下数码管的连接方法。 数码管的8根LED显示…

解决Windows更新后无法启动的十种办法,总有一种适合你

你可能已经更新了操作系统以修复错误或使用最新功能。但是,如果Windows在更新后无法启动呢? 如果你面临这样的问题,主要是由于安装文件中的错误或你的系统与最新更新不兼容。此外,损坏的MBR或驱动程序也会阻止电脑启动。 不管是什么原因,本文将用十种简单的技术来指导你…

【蓝桥杯单片机入门记录】认识单片机

目录 单片机硬件平台 单片机的发展过程 单片机开发板 单片机基础知识 电平 数字电路中只有两种电平&#xff1a;高和低 二进制&#xff08;8421码&#xff09; 十六进制 二进制数的逻辑运算 “与” “或” “异或” 标准C与C51 如何学好单片机 端正学习的态度、培…

2024年云南省考报名详细流程图解,招聘5710人!

云南省考公告出来了&#xff01;招5710人&#xff01; ✔️报名时间&#xff1a;2024年2月19日9:00至2月23日18:00 ✔️缴费时间&#xff1a;2024年2月20日0:00至2月25日24:00 ✔️公共科目笔试时间为&#xff1a; 2024年3月16日上午 9:00&#xff0d;11:00 行政职业能力测验 2…

【leetcode热题100】不同的二叉搜索树

给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;5示例 2&#xff1a; 输入&#xff1a;n 1 输出&#xff1a;1 …

平时积累的FPGA知识点(7)

平时在FPGA群聊等积累的FPGA知识点&#xff0c;第七期&#xff1a; 11 描述扇出的xilinx官方文档是&#xff1f; 解释&#xff1a;ug949 12 在BD中如何指定某个IP用global&#xff0c;其他的用OOC模式&#xff1f;因为某个模块引用的IP带着XPM&#xff0c;综合不了 解释&am…

【MySQL】高度为2和3时B+树能够存储的记录数量的计算过程

文章目录 题目答案高度为2时的B树高度为3时的B树总结 GPT4 对话过程 题目 InnoDB主键索引的Btree在高度分别为 2 和 3 时&#xff0c;可以存储多少条记录&#xff1f; 答案 高度为2时的B树 计算过程&#xff1a; 使用公式 ( n 8 ( n 1 ) 6 16 1024 ) (n \times 8 …

ELAdmin 隐藏添加编辑按钮

使用场景 做了一个监控模块&#xff0c;数据都是定时生成的&#xff0c;所以不需要手动添加和编辑功能。 顶部不显示 可以使用 true 或者 false 控制现实隐藏 created() {this.crud.optShow {add: false,edit: false,del: true,download: true,reset: true}},如果没有 crea…

python守护进程--supervisor 使用教程

supervisor 使用教程python守护进程1.安装 pip3 install supervisor -i https://pypi.tuna.tsinghua.edu.cn/simple 2.使用supervisor 启动 python main.py 文件 vim /etc/supervisor/conf.d/demo.conf添加以下内容&#xff1a;[program:demo] #项目名称为democommandp…

oppo手机QQ上传文件所在位置

一、打开手机“文件管理”APP 点击“点击查看”&#xff0c;按钮&#xff0c;会进入到新的根目录。 寻找下面的目录进入

StarUML无法安装扩展的解决方案

StarUML无法安装扩展解决方案 版本&#xff1a;StarUML3.2.2 遇到问题 Unable to access the extension registry, Please try again later. 解决方案 第一步 https://docs.staruml.io/user-guide/managing-extensions#install-extension官网给了怎么手动安装扩展器的方法…

【leetcode】深搜、暴搜、回溯、剪枝(C++)2

深搜、暴搜、回溯、剪枝&#xff08;C&#xff09;2 一、括号生成1、题目描述2、代码3、解析 二、组合1、题目描述2、代码3、解析 三、目标和1、题目描述2、代码3、解析 四、组合总和1、题目描述2、代码3、解析 五、字母大小写全排列1、题目描述2、代码3、解析 六、优美的排列1…

【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱5(附带项目源码)

效果演示 文章目录 效果演示系列目录前言制作系统定义制作配方 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第25篇中&#xff0c;我们将探索如何用unity制作一个3D背包、库存、制…

(四)【Jmeter】 JMeter的界面布局与组件概述

JMeter的界面布局 中文版&#xff1a; 英文版&#xff1a; JMeter的主界面包括菜单栏、工具栏、树形结构面板、视图面板等部分。 菜单栏&#xff1a;菜单栏包含了文件(File)、编辑(Edit)、查找(Search)、选项(Options)、工具(Tools)、帮助(Help)等菜单项&#xff0c;用于对…