安防监控项目---web网页通过A9控制Zigbee终端节点的风扇

news2025/7/22 21:48:28

文章目录

  • 前言
  • 一、zigbee的CGI接口
  • 二、请求线程和硬件控制
  • 三、现象展示
  • 总结


前言

书接上期,我们可以看一下前面的功能设计的部分,网页端的控制还有一个,那就是通过网页来控制zigbee上的风扇节点,这部分的工作量是相当大的,既要实现HTML发送控制命令到A9平台进行接收,又要实现A9平台串口通信控制zigbee协调器,通过zigbee协调器控制zigbee终端节点(这一步是建立在zigbee协调器和终端节点调试好的基础上的);最终呢实现网页控制zigbee节点,这也实现了无线控制!


一、zigbee的CGI接口

第一步呢还是先来看命令字是怎样下发的:

#include <stdio.h> 
#include "cgic.h" 
#include <string.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define N 8
struct msg
{
	long type;
	long msgtype;
	unsigned char text[N];
};
int cgiMain() 
{ 
	key_t key;
	char buf[N];
	char sto_no[2];
	int msgid;
	struct msg msg_buf;
	memset(&msg_buf,0,sizeof(msg_buf));
	
	cgiFormString("fan",buf,N);
	cgiFormString("store",sto_no,2);

	if((key = ftok("/tmp", 'g')) < 0)
	{
		perror("ftok");
		exit(1);
	}

	if((msgid = msgget(key, 0666)) < 0)
	{
		perror("msgget");
		exit(1);
	}
	bzero (msg_buf.text, sizeof (msg_buf.text));

	switch (buf[0])
	{
		case '0':
			{
				msg_buf.text[0] = (0 << 6) | (2 << 4) | (0 << 0);
				break;
			}
		case '1':
			{
				msg_buf.text[0] = (0 << 6) | (2 << 4) | (1 << 0);
				break;
			}
	}
	
	msg_buf.type = 1L;
	msg_buf.msgtype = 4L;
	msgsnd(msgid, &msg_buf,sizeof(msg_buf)-sizeof(long),0);

	sto_no[0] -= 48;


	cgiHeaderContentType("text/html\n\n"); 
	fprintf(cgiOut, "<HTML><HEAD>\n"); 
	fprintf(cgiOut, "<TITLE>My CGI</TITLE></HEAD>\n"); 
	fprintf(cgiOut, "<BODY>"); 

	fprintf(cgiOut, "<H2>send sucess</H2>");

	//fprintf(cgiOut, "<a href='.html'>返回</a>"); 
	fprintf(cgiOut, "<meta http-equiv=\"refresh\" content=\"1;url=../a9_zigbee%d.html\">", sto_no[0]);
	fprintf(cgiOut, "</BODY>\n"); 
	fprintf(cgiOut, "</HTML>\n"); 
	return 0; 
} 

这里呢我也没有给出过多的注释,其实流程和前面的led和beep的代码基本一致,且框架结构也都一致;这就是一个好的框架所带来的优势,使得开发起来比较容易;

二、请求线程和硬件控制

请求线程只需要在switch…case语句中添加控制类型即可;这里控制的流程直接写在了switch中,也可以将其写在单独的处理线程中;这里用到了Linux下的串口编程,所以需要包含头文件linuxuart.h这个头文件;

#include "data_global.h"
#include "linuxuart.h"

//消息队列id
extern int msgid;
//ipc对象键值
extern key_t key;
//锁资源
extern pthread_mutex_t mutex_client_request,
        		mutex_refresh,
        		mutex_sqlite,
	        	mutex_transfer,
	        	mutex_analysis,
	        	mutex_sms,
	        	mutex_buzzer,
	         	mutex_led,
	         	mutex_camera;
//条件变量
extern pthread_cond_t  cond_client_request,
        		cond_refresh,
        		cond_sqlite,
	        	cond_transfer,
	        	cond_analysis,
	        	cond_sms,
	        	cond_buzzer,
	         	cond_led,
	         	cond_camera;
//模块的控制命令字
extern unsigned char cmd_led;
extern unsigned char  cmd_buzzer;
extern unsigned char  cmd_fan;

//GPRS模块的电话号
extern char recive_phone[12] ;
extern char center_phone[12] ;

//消息队列通信结构体
struct msg msgbuf;

void *pthread_client_request(void *arg)
{
	if((key = ftok("/tmp",'g')) < 0){
		perror("ftok failed .\n");
		exit(-1);
	}

	msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666); 		//检测消息队列中是否有这个键值,如果有则返回对应的-1,没有则创建并返回创建消息队列的id
	if(msgid == -1)	{
		if(errno == EEXIST){ 							//如果已经存在
			msgid = msgget(key,0777); 					//设置权限为0777
		}else{
			perror("fail to msgget");
			exit(1);
		}
	}
	printf("pthread_client_request\n");
	
	while(1){
		bzero(&msgbuf,sizeof(msgbuf)); 					//清理操作,但一般使用memset,功能更加强大一点
		printf("wait form client request...\n"); 
		msgrcv (msgid, &msgbuf, sizeof (msgbuf) - sizeof (long), 1L, 0); //从消息队列中读取消息
		printf ("Get %ldL msg\n", msgbuf.msgtype); 		//打印消息类型
		printf ("text[0] = %#x\n", msgbuf.text[0]); 	//打印消息内容

		//判断消息类型,从而确定是哪一个设备
		switch(msgbuf.msgtype){
			case 1L:
				//1L的类型是led的消息类型,此时上锁,等待消息内容也就是控制命令字复制完成后解锁,通过pthread_cond_signal唤醒pthread_led.c这个led线程,进行led的具体硬件操作
				pthread_mutex_lock(&mutex_led);
				printf("hello led\n");
				cmd_led = msgbuf.text[0];
				pthread_mutex_unlock(&mutex_led);
				pthread_cond_signal(&cond_led);
				break;
			case 2L:
				//2L表示beep的消息类型
				//这里呢相信大家天赋异禀,看懂了上面led的,beep也不是问题
				pthread_mutex_unlock(&mutex_buzzer);
				printf("hello beep1\n");
				pthread_mutex_lock(&mutex_buzzer);
				cmd_buzzer = msgbuf.text[0];
				pthread_mutex_unlock(&mutex_buzzer);
				pthread_cond_signal(&cond_buzzer);
				break;
			case 3L:
				pthread_mutex_lock(&mutex_led);
				printf("hello seg\n");
				cmd_seg = msgbuf.text[0];
				pthread_mutex_unlock(&mutex_led);
				pthread_cond_signal(&cond_led);
				break;
			case 4L:
				pthread_mutex_lock(&mutex_sqlite);
				printf("hello fan\n");
				cmd_fan = msgbuf.text[0];
				
				int fan_fd = open_port("/dev/ttyUSB0"); //打开设备
				if(fan_fd < 0){
					printf("open failed\n");
				}
				set_com_config(fan_fd, 115200, 8, 'N', 1); //设置串口参数
				
				char cmdbuf[4] = {0};
				if(cmd_fan == 0x21){
					strcpy(cmdbuf,"1\n"); //注意点,一定要加上\n,因为串口助手是默认添加\n的,所以这里我们需要手动添加
					write(fan_fd,cmdbuf,sizeof(cmdbuf)/sizeof(cmdbuf[0]));
					sleep(2);
				}
				if(cmd_fan == 0x20){
					strcpy(cmdbuf,"0\n");
					write(fan_fd,cmdbuf,sizeof(cmdbuf)/sizeof(cmdbuf[0]));
					sleep(2);
				}
				char buf[32] = {0};
				//get data from zigbee
				read(fan_fd,&buf,sizeof(buf));
				printf("sizeof(buf) = %d.\n",sizeof(buf));
				printf(">>>>>>%s\n",buf);
				sleep(1);
				pthread_mutex_unlock(&mutex_sqlite);
				break;
			default:
				break;
		}
	}
}
#endif 

接下来看一下linux下的串口编程,为什么要学习这个呢,其实就是要通过USB去和zigbee模块通信,那么无非就是设置通信速率,校验位,停止位等这些参数:
先看一下linuxuart.h文件:

#ifndef __LINUX_UART_H_
#define __LINUX_UART_H_

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include <termios.h>
#include <string.h>
#include "data_global.h"

extern int set_com_config(int fd, int baud_rate, int data_bits, char parity, int stop_bits);
extern int open_port(char *com_port);
extern void USB_UART_Config(char* path, int baud_rate);
#endif

再来看一下linuxuatr.c文件:

#include "linuxuart.h"

int set_com_config(int fd, int baud_rate, int data_bits, char parity, int stop_bits)
{
	struct termios new_cfg, old_cfg;
	int speed;
	/*保存原有串口配置*/
	if (tcgetattr(fd, &old_cfg) != 0){
		perror("tcgetattr");
		return -1;
	}

	new_cfg =old_cfg;

	/*配置为原始模式*/
	cfmakeraw(&new_cfg);
	new_cfg.c_cflag &= ~CSIZE;

	/*设置波特率*/
	switch (baud_rate)
	{
		case 2400:{
					  speed = B2400;
					  break; 
				  }
		case 4800:{
					  speed = B4800;
					  break;
				  }
		case 9600:{
					  speed = B9600;
					  break;
				  }
		case 19200:{
					   speed = B19200;
					   break;
				   }
		case 38400:{
					   speed = B38400;
					   break;
				   }

		default:
		case 115000:{
						speed = B115200;
						break;
					}
	}

	cfsetispeed(&new_cfg, speed);
	cfsetospeed(&new_cfg, speed);

	/*设置数据位*/
	switch (data_bits)
	{
		case 7:{
				   new_cfg.c_cflag |= CS7;
				   break;
			   }   
		default:	
		case 8:{
				   new_cfg.c_cflag |= CS8;
				   break;
			   }
	}

	/*设置奇偶校验位*/
	switch (parity)
	{
		default:
		case 'n':
		case 'N':{
					 new_cfg.c_cflag &= ~PARENB;
					 new_cfg.c_iflag &= ~INPCK;
					 break;
				 }
		case 'o':
		case 'O':{
					 new_cfg.c_cflag |= (PARODD |PARENB);
					 new_cfg.c_iflag |= INPCK;
					 break;
				 }
		case 'e':
		case 'E':{
					 new_cfg.c_cflag |= PARENB;
					 new_cfg.c_cflag &= ~PARODD;
					 new_cfg.c_iflag |= INPCK;
					 break;
				 }
		case 's':
		case 'S':{
					 new_cfg.c_cflag &= ~PARENB;
					 new_cfg.c_cflag &= ~CSTOPB;
					 break;
				 }
	}

	/*设置停止位*/
	switch (stop_bits)
	{
		default:
		case 1:{
				   new_cfg.c_cflag &= ~CSTOPB;
				   break;
			   }   	
		case 2:{
				   new_cfg.c_cflag |= CSTOPB;
				   break;
			   }
	}

	/*设置等待时间和最小接收字符*/
	new_cfg.c_cc[VTIME] = 0;
	new_cfg.c_cc[VMIN] = 1;
	tcflush(fd, TCIFLUSH);
	if ((tcsetattr(fd, TCSANOW, &new_cfg)) != 0)
	{
		perror("tcsetattr");
		return -1;
	}

	return 0;
}


int open_port(char *com_port)
{
	int fd;

	/*打开串口*/
	fd = open(com_port, O_RDWR|O_NOCTTY|O_NDELAY);
	if (fd < 0){
		perror("open serial port");
		return -1;
	}

	/*恢复串口阻塞状态*/
	if (fcntl(fd, F_SETFL, 0) < 0){
		perror("fcntl F_SETFL\n");
	}

	/*判断是否为终端设备*/
	if (isatty(fd) == 0){
		perror("This is not a terminal device");
	}

	return fd;
}


/*--------------------CH340Ƥ׃---------------------------*/
void USB_UART_Config(char* path, int baud_rate)
{
	int fd;
	fd = open_port(path);
	if(fd < 0){
		printf("open %s failed\n",path);
		return ;
	}
	if (set_com_config(fd, baud_rate, 8, 'N', 1) < 0)
	{
		perror("set_com_config");
		return ;
	}
	close(fd);
	return ;
}

这些函数是不是挺熟悉的,其实就是我们经常使用的串口助手呗!软件化了,没有图形化界面了!

三、现象展示

风扇静止的时候如下:
请添加图片描述
下图是风扇转动起来的图片:
请添加图片描述
下图是终端显示的控制命令字:
打开显示:
请添加图片描述
关闭显示:
请添加图片描述
这里也可以看出跟代码信息是一致的;消息类型是4L,控制命令字为0x21和0x20;且打印信息也正确;
在这里插入图片描述


总结

本期的分享就到这里结束了,需要注意的是zigbee大家必须先调整好,保证其正常工作,进而学习Linux串口编程,再将这些融合到项目中,就能够实现网页控制zigbee终端节点硬件了;最后,各位小伙伴们如果有收获,可以点赞收藏哦,你们的认可是我创作的动力,一起加油!

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

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

相关文章

管理类联考——英语二——阅读篇——题材:心理

文章目录 2013年&#xff0c;Text 3——题材&#xff1a;心理细节题&#xff08;难&#xff09;细节题——排除法细节题细节题观点态度题 2015 年&#xff0c;Text 1——题材&#xff1a;心理细节题细节题推断题词义句意题细节题 2019 年&#xff0c;Text 1——题材&#xff1a…

八、W5100S/W5500+RP2040树莓派Pico<DNS>

文章目录 1 前言2 协议简介2.1 什么是DNS2.2 DNS的优点2.3 DNS工作原理2.4 应用场景 3 WIZnet以太网芯片4 DNS网络设置示例概述以及使用4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 烧录验证 5 注意事项6 相关链接 1 前言 为了更好地支持应用程序的性能和可用性…

RuoYi若依源码分析1 - security

Security springsecurity配置文件夹 security springsecurity总配置类 SecurityConfig.java SecurityConfig 总配置分析 首先看一下总配置&#xff0c;我们可以从总配置项里面大体的总结出springsecurity鉴权在ruoyi框架里面是如何执行的 自动装配关键处理类以及过滤器等…

爬虫 | 【实践】百度搜索链接爬取,生成标题词云 | 以“AI换脸”为例

目录 &#x1f4da;链接爬取 &#x1f407;流程梳理 &#x1f407;代码实现 &#x1f407;结果 &#x1f4da;词云生成 &#x1f407;代码实现 &#x1f407;结果 &#x1f4da;链接爬取 &#x1f407;流程梳理 总体流程是&#xff1a;构建搜索链接 -> 发送HTTP请求…

解决Linux Debian12系统中安装VirtualBox虚拟机无法使用USB设备的问题

Debian12系统中安装VirtualBox&#xff0c;再VirtualBox虚拟机中无法使用 USB设备。如下图所示&#xff1a; 解决方法如下&#xff1a; 1.安装 Virtualbox增强功能。如下图所示&#xff1a; 2.添加相关用户、用户组&#xff08; Virtualbox 装完成后会有 vboxusers 和 vboxs…

初学编程学什么语言,中文编程系统化教程课程之自定义图形窗口自定义标题栏编程,零基础学编程轻松学编程

初学编程学什么语言&#xff0c;中文编程系统化教程课程之自定义图形窗口自定义标题栏编程&#xff0c;零基础学编程轻松学编程 该编程工具开发的系列管理软件 编程系统化课程总目录及明细&#xff0c;零基础学编程视频教程&#xff0c;点击进入了解详情。 https://blog.csdn.n…

无法ping通ECS服务器公网IP的排查方法

无法ping通ECS实例的原因较多&#xff0c;您可以参考九河云编辑的文章进行排查。 问题现象 本地客户端无法ping通目标ECS实例公网IP&#xff0c;例如&#xff1a; 本地客户端为Linux系统&#xff0c;ping目标ECS实例公网IP时无响应&#xff0c;如下所示&#xff1a; 本地客…

Java日志组件之三Log4j2漏洞剖析及重现

一、前言 这一篇我们来介绍一下史上第二严重的安全漏洞是个什么情况&#xff0c;原理是什么&#xff0c;如何重现。 二、Log4j2 Lookup机制 Log4j2 Lookup机制最重要的功能就是提供一个可扩展的方式让你可以添加某些特殊的值到日志中。你调用logger.info(name);这样的语句&a…

【赠书第1期】零基础学无人机航拍与短视频后期剪辑实战教程

文章目录 前言 1 购买前须知 2 准备工作 3 飞行控制 4 拍摄技巧 5 安全提示 6 推荐图书 7 粉丝福利 前言 随着科技的不断发展&#xff0c;无人机已经不再是军事装备的专属&#xff0c;它已经成为了消费级无人机的主流产品。作为国内领先的无人机生产商&#xff0c;大疆…

【腾讯云 HAI域探秘】StableDiffusionWebUI一小时搞定100张设计图

目录 前言一、选择 HAI部署的优势二、HAI 搭建AI绘图服务实现思路三、生成设计图操作流程1、新建HAI应用2、StableDiffusionWebUI&#xff08;1&#xff09;功能介绍&#xff08;2&#xff09;页面转中文&#xff08;3&#xff09;线稿生成图 四、部署StableDiffusionWebUI服务…

通讯网关软件033——利用CommGate X2OPC实现PI数据写入OPC Server

本文推荐利用CommGate X2OPC实现从PI服务器读取数据并写入OPC Server。CommGate X2OPC是宁波科安网信开发的网关软件&#xff0c;软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示&#xff0c;实现从PI实时数据库获取数据并写入OPC Server。 【解决…

maven环境变量的配置

windows系统 1. win键 r&#xff0c;输入sysdm.cpl打开系统属性界面&#xff0c;选择高级栏目&#xff0c;点击环境变量菜单打开环境变量界面。 2. 选择系统变量下的新建菜单&#xff0c;变量名输入MAVEN_HOME&#xff0c;变量值输入maven的安装目录&#xff0c;例如&#xff…

CNN卷积神经网络模型的GPU显存占用分析

一、参考资料 浅谈深度学习:如何计算模型以及中间变量的显存占用大小 如何在Pytorch中精细化利用显存 二、相关介绍 0. 预备知识 为了方便计算&#xff0c;本文按照以下标准进行单位换算&#xff1a; 1 G 1000 MB1 M 1000 KB1 K 1000 Byte1 B 8 bit 1. 模型参数量的计…

基于构件的开发(CBD)

基于构件的开发&#xff08;Component-Based Development&#xff0c;简称CBD&#xff09;或基于构件的软件工程&#xff08;Component-Based Software Engineering&#xff0c;简称CBSE&#xff09;是一种软件开发新范型&#xff0c;它是在一定构件模型的支持下&#xff0c;复…

FPGA与ASIC有什么差异?二者该如何选用?

前言 对于一个数字电路的新手来说&#xff0c;这可能是会经常遇到的一个问题&#xff1a;FPGA和ASIC之间的区别是什么? 接下来本文将尝试讲解 “什么是FPGA&#xff1f;” 和 “什么是ASIC&#xff1f;”&#xff0c;然后讲述一些关于FPGA和ASIC的问题&#xff0c;例如它们之间…

【【FIFO to multiplier to RAM的 verilog 代码 和 testbnench 】】

FIFO to multiplier to RAM的 verilog 代码 和 testbnench 只完成了单个数据的传输 大数据需要修改 tb 或者基本连线 FIFO.v //synchronous fifo module FIFO_syn #(parameter WIDTH 16, // the fifo wideparameter DEPTH 1024, …

【MATLAB】全网唯一的7种信号分解+ARIMA联合的时序预测算法全家桶

有意向获取代码&#xff0c;请转文末观看代码获取方式~ 大家吃一顿火锅的价格便可以拥有7种信号分解ARIMA组合的时序预测算法&#xff0c;绝对不亏&#xff0c;知识付费是现今时代的趋势&#xff0c;而且都是我精心制作的教程&#xff0c;有问题可随时反馈~也可单独获取某一算…

【三方登录-Apple】iOS 苹果授权登录(sign in with Apple)之开发者配置一

记录一下sign in with Apple的开发者配置 前言 关于使用 Apple 登录 使用“通过 Apple 登录”可让用户设置帐户并使用其Apple ID登录您的应用程序和关联网站。首先使用“使用 Apple 登录”功能启用应用程序的App ID 。 如果您是首次启用应用程序 ID 或为新应用程序启用应用程序…

加速计算卡设计方案:389-基于KU5P的双路100G光纤网络加速计算卡

基于KU5P的双路100G光纤网络加速计算卡 一、板卡概述 基于Xilinx UltraScale16 nm KU5P芯片方案基础上研发的一款双口100 G FPGA光纤以太网PCI-Express v3.0 x8智能加速计算卡&#xff0c;该智能卡拥有高吞吐量、低延时的网络处理能力以及辅助CPU进行网络功能卸载的能力…

SpringBoot配置文件yml文件基础知识

yaml简介 YAML&#xff08;YAML Aint Markup Language&#xff09;&#xff0c;一种数据序列化格式 优点&#xff1a;容易阅读容易与脚本语言交互以数据为核心&#xff0c;重数据轻格式 YAML文件扩展名.yml&#xff08;主流&#xff09;.yamlyaml语法规则 大小写敏感 属性层级…