一.项目介绍
1。智能家具以智能电视柜的网络中心、数据中心为核心,利用移动设备内嵌自主研发的APP,使用WIFI传输协议、蓝牙传输协议进行命令集传送及数据交换,以终端IC内嵌WIFI、蓝牙控制协议实现对家具的无线控制功能。使用户可方便的通过手机进行数据交换、数据传送、影音播放、功能控制。
2.本项目主要为只能家具的蓝牙网关模块(接收手机端的消息,通过TCP/ud/mqtt协议发送给蓝牙网关。再转发到对应的端口)
3.以下是本次需要用到的函数
void get_local_time(char *buf, int bufsize);//获取当前时间
void *tcp_resc(void *arg);//tcp接收往蓝牙
void *udp_resc(void *arg);//udp接收往蓝牙
void *mqtt_resc(void *arg);//mqtt接收往蓝牙
int configure_serial(int fd);
void bule_send(unsigned char *ip,unsigned char *buf);//接收蓝牙发的信息的线程函数
void my_msq_callback(struct mosquitto *msq,void * obj,const struct mosquitto_message *msg);
void *resc_pthread(void *arg);//接收蓝牙往其他端发线程
void tcp_send(char *ip_buf,char *port_buf,char *resc_buf);//将蓝牙的消息转发
void udp_send(char *ip_buf,char *port_buf,char *resc_buf);//将蓝牙的消息转发
void mqtt_send(char *ip_buf,char *port_buf,char *resc_buf);//将蓝牙的消息转发,并存到数据库
void my_sql(int arg1,char *arg2);
二.通讯功能实现
1.蓝牙的接收与发送
接收手机端蓝牙发送的消息
int fd; // 文件描述符用于串行通信
struct termios options; // 用于存储和配置串行端口设置的结构
unsigned char buffer[256]; // 用于读取串口数据的缓冲区
int bytes_read; // 从串口读取的字节数
fd = open("/dev/ttyUSB2", O_RDWR | O_NOCTTY | O_NDELAY); // 尝试打开串行端口ttyUSB0
if (fd == -1) { // 如果打开失败
perror("Unable to open port"); // 打印错误信息
return 1; // 退出程序
} else {
fcntl(fd, F_SETFL, 0); // 设置文件描述符属性
printf("Port is open.\n"); // 端口打开成功,打印消息
}
tcgetattr(fd, &options); // 获取当前设备的配置
cfsetispeed(&options, B115200); // 设置输入波特率为115200
cfsetospeed(&options, B115200); // 设置输出波特率为115200
options.c_cflag &= ~CSIZE; // 清除数据位
options.c_cflag |= CS8; // 设置数据位为8
options.c_cflag &= ~PARENB; // 无奇偶校验
options.c_cflag &= ~CSTOPB; // 停止位为1
options.c_iflag &= ~(IXON | IXOFF | IXANY); // 不使用软件流控制
options.c_cflag &= ~CRTSCTS; // 不使用硬件流控制
options.c_cflag |= (CLOCAL | CREAD); // 忽略调制解调器的状态线,使能接收
tcsetattr(fd, TCSANOW, &options); // 将修改后的配置应用到设备
printf("Port is configured.\n"); // 打印端口配置完成的消息
while (1) { // 无限循环
tcflush(fd, TCIFLUSH); // 清空输入缓冲区
bytes_read = read(fd, buffer, sizeof(buffer) - 1); // 从串行端口读取数据
if (bytes_read < 0) {
perror("Error reading from serial port"); // 如果读取时发生错误,则打印错误信息
break; // 跳出无限循环
}
printf("%s",buffer);
}
close(fd); // 关闭串行端口
将电脑端消息通过蓝牙发送给手机
int configure_serial(int fd) {
struct termios options;
tcgetattr(fd, &options); // 获取当前设备的配置
cfsetispeed(&options, B115200); // 设置输入波特率为115200
cfsetospeed(&options, B115200); // 设置输出波特率为115200
options.c_cflag &= ~CSIZE; // 清除数据位
options.c_cflag |= CS8; // 设置数据位为8
options.c_cflag &= ~PARENB; // 无奇偶校验
options.c_cflag &= ~CSTOPB; // 停止位为1
options.c_iflag &= ~(IXON | IXOFF | IXANY); // 不使用软件流控制
options.c_cflag &= ~CRTSCTS; // 不使用硬件流控制
options.c_cflag |= (CLOCAL | CREAD); // 忽略调制解调器的状态线,使能接收
tcsetattr(fd, TCSANOW, &options); // 将修改后的配置应用到设备
return 0;
}
int fd;
unsigned char data_to_send[] = {0x11, 0x34, 0x0D}; // 要发送的数据
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY); // 尝试打开串行端口ttyUSB0
if (fd == -1) {
perror("Unable to open port");
return 1;
} else {
fcntl(fd, F_SETFL, 0);
printf("Port is open.\n");
}
configure_serial(fd); // 配置串行端口
printf("Port is configured.\n");
printf("Sending data...\n");
ssize_t bytes_written = write(fd, data_to_send, sizeof(data_to_send)); // 将数据写入串行端口
if (bytes_written < 0) {
perror("Error writing to serial port");
} else {
printf("Data sent successfully.\n");
}
close(fd); // 关闭串行端口
2.mqtt的发送。
void mqtt_send(char *ip_buf,char *port_buf,char *resc_buf)
{
int temp=atoi(resc_buf);
char time_buf[22]="";
int bufsize = sizeof(time_buf);
get_local_time(time_buf, bufsize);
my_sql(temp,time_buf);
mosquitto_lib_init();
struct mosquitto *msq;
msq=mosquitto_new("pub",true,port_buf);
mosquitto_connect(msq,ip_buf,1883,2);
mosquitto_publish(msq,NULL,port_buf,strlen(resc_buf),resc_buf,1,false);
// 断开MQTT连接并清理资源
mosquitto_disconnect(msq);
mosquitto_destroy(msq);
mosquitto_lib_cleanup();
}
3.udp接收与发送
这里用将改功能写入线程函数,来保证电脑终端运行时一直在接收udp协议发送来的消息,当接收到消息后通过蓝牙转发到手机端
void *udp_resc(void *arg)
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("sockfd");
return NULL;
}
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(g_port);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
while (1)
{
struct sockaddr_in cli_addr; // 定义客户端地址结构体
socklen_t cli_len = sizeof(cli_addr); // 定义地址结构体的长度
unsigned short port = 0; // 定义用于存储端口的变量
unsigned char ip[16] = ""; // 定义用于存储IP的数组
unsigned char buf[128] = "";
int len = 0;
len = recvfrom(sockfd, buf,sizeof(buf), 0, (struct sockaddr *)&cli_addr, &cli_len);
if(len>0)
{
printf("*********udP*******\n");
port = ntohs(cli_addr.sin_port); // 获取端口号
inet_ntop(AF_INET, &cli_addr.sin_addr.s_addr,ip, 16); // 获取IP地址
printf("%s:%hu %s\n", ip, port, buf); // 打印客户端的IP、端口和发送的数据
bule_send(ip, buf);
}
}
return NULL; // 线程返回
}
当接收消息的线程接收到消息后,进行拆包,判断需要什么协议来转发这条消息,当判断为需要udp协议时,运行该函数
void udp_send(char *ip_buf,char *port_buf,char *resc_buf)
{
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
int flag=1;
if(sockfd<0)
{
perror("socket:");
return;
}
struct sockaddr_in my_addr;
bzero(&my_addr,sizeof(my_addr));
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(atoi(port_buf));
inet_pton(AF_INET,ip_buf,&my_addr.sin_addr.s_addr);
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&flag,sizeof(flag));
sendto(sockfd,resc_buf,strlen(resc_buf),0,(struct sockaddr *)&my_addr,sizeof(my_addr));
close(sockfd);
}
TCP的接收和发送
和udp一样,tcp,mqtt,udp三个接收线程当接收到消息的时候调用蓝牙的送函数,转发其他端发来的消息
void *tcp_resc(void *arg)
{
int sockfd= socket(AF_INET,SOCK_STREAM,0);
if (sockfd<0)
{
perror("sockfd:");
return NULL;
}
//printf("sockfd=%d\n",sockfd);
//2.连接服务器connect
//创建目的地址结构体
struct sockaddr_in dst_addr;
bzero(&dst_addr, sizeof(dst_addr));
dst_addr.sin_family = AF_INET;
dst_addr.sin_port = htons(g_port);
inet_pton(AF_INET, "ip", &dst_addr.sin_addr.s_addr);
//使用连接函数连接
connect(sockfd,(struct sockaddr *)&dst_addr,sizeof(dst_addr));
//3.具体的功能
while (1)
{
struct sockaddr_in cli_addr; // 定义客户端地址结构体
socklen_t cli_len = sizeof(cli_addr); // 定义地址结构体的长度
unsigned char ip[16] = ""; // 定义用于存储IP的数组
unsigned char buf[128] = "";
int len = 0;
len=recv(sockfd, buf,sizeof(buf), 0);
if (len>0)
{
printf("*********TCP*******\n");
printf("%s %s\n", ip, buf); // 打印客户端的IP、端口和发送的数据
bule_send(ip, buf);
}
}
close(sockfd);
return NULL;
}
void tcp_send(char *ip_buf,char *port_buf,char *resc_buf)
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in dst_addr;
bzero(&dst_addr,sizeof(dst_addr));
dst_addr.sin_family=AF_INET;
dst_addr.sin_port=htons(atoi(port_buf));
inet_pton(AF_INET,ip_buf,&dst_addr.sin_addr.s_addr);
connect(sockfd,(struct sockaddr*)&dst_addr,sizeof(dst_addr));
send(sockfd,resc_buf,strlen(resc_buf),0);
return;
}
三.其他功能
1.数据库功能
连接数据库,将收到的信息存入到数据库
void history_sql(char *arg1,char *arg2,char *arg3,char *arg4)
{
MYSQL mysql, *sock; // 定义mysql实例和指针
//初始化数据库环境
mysql_init(&mysql); // 用于初始化MYSQL对象,为后续的连接做准备。参数是要初始化的MYSQL对象的地址。
//连接数据库
sock = mysql_real_connect(&mysql, linux_ip, "名字", "1密码", "mqtt", 3306, NULL, 0);
// 这个函数用于创建与MySQL服务器的连接。参数依次为:MYSQL对象的地址、服务器的IP地址、用户名、密码、数据库名、服务器的端口号、unix套接字(在这里没有使用,所以为NULL)、客户端标志(在这里没有使用,所以为0)
if (sock == NULL)
{
fprintf(stderr, "连接失败: %s\n", mysql_error(&mysql));
return;
}
char sql_str[512]; // 或者根据实际情况分配足够的内存空间
sprintf(sql_str, "insert into history(agr,ip,port,time) values('%s','%s','%s','%s')", arg1, arg2,arg3,arg4);
int ret=mysql_real_query(&mysql,sql_str,strlen(sql_str));
if(ret !=0)
{
perror("mysql_query:");
return;
}
MYSQL_RES *res=mysql_store_result(&mysql);
mysql_free_result(res);
mysql_close(sock);
}
2.实时时间显示
获取当前系统的时间
void get_local_time(char *buf, int bufsize) {
// 获取当前时间
time_t rawtime;
struct tm *timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
// 将时间格式化为字符串
strftime(buf, bufsize, "%Y-%m-%d %H:%M:%S", timeinfo);
}