安防监控项目---web点灯(网页发送命令控制A9的led)

文章目录

  • 前言
  • 一、web点亮LED流程
  • 二、静态网页设计(html界面)
  • 三、 CGI和BOA在本项目中的使用
  • 总结


前言

书接上期,和大家分享的是web点灯,哈哈哈,谈论起点灯这个词,这么久以来我已然已经成长为一名合格的点灯大师了;点灯是一个很好的测试办法,不仅要去测试开发板是否正常,也要去测试网页是否能够顺利下发数据,接下俩让我们仔细来看一下这个过程!!!


一、web点亮LED流程

先来理一下流程,之后呢我们按照这个流程来一步步实现相应的功能;

html的数据发送到A9期间经历了这么几个过程:
在这里插入图片描述
接下来咱们就根据上面流程图一步步来看一下每一步的具体实现!

二、静态网页设计(html界面)

以下呢就是默认index.html网页的具体设计;代码还是比较简单的,没啥技术含量,大家只要能读懂,在这个基础上可以根据自己的需求去修改就可以了;

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>登录界面</title></head><body><body  background="./images/ckp00.png"><div align="center"><table width="900" border="0" background="./images/ckp3.png"><tr><th width="385" height="320" scope="col">&nbsp;</th><th width="385" scope="col">&nbsp;</th><th width="70" scope="col">&nbsp;</th></tr><tr><td height="50">&nbsp;</td><td><div align="center"></div></td><td>&nbsp;</td></tr></table></div><div></div><div align="center"><table width="900" border="0"><tr><td><form onsubmit="return isValidate(myform)" action="cgi-bin/login.cgi" method="post">用户名: <input type="text" name="username" id="username"> 密码: <input type="password" name="userpass" id="userpass"><input type="submit" value="登录" id="button"></form></td></tr></table></div><!--<p>&nbsp;</p>--><div align="center"><table width="900" height="467" border="0" background="./images/ckp4.jpg"><tr><td width="126" height="248">&nbsp;</td><td width="351"></td><td width="101">&nbsp;</td></tr><tr><td></td><td>&nbsp;</td></tr><tr><td><form name="myform" method="get" action="cgi-bin/login.cgi" onsubmit="return isValidate(myform)"></form></td></tr></div></body></html>

网页打开如下:
在这里插入图片描述
在这里呢需要用户名和密码来进行登录;那接下来肯定是看一下怎样进行登录了呀!

三、 CGI和BOA在本项目中的使用

用户名和密码由网页端发送BOA服务器,接收到数据后CGI进行数据的解析,那接下来看下CGI的程序到底是如何编写的!

#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>   char name[64];   
char pass[64];  char* getcgidata(FILE* fp, char* requestmethod)   
{   char* input;   int len;   int size = 1024;   int i = 0;   if (!strcmp(requestmethod, "GET"))   {   input = getenv("QUERY_STRING");   return input;   }   else if (!strcmp(requestmethod, "POST"))   {   len = atoi(getenv("CONTENT_LENGTH"));   input = (char*)malloc(sizeof(char)*(size + 1));   if (len == 0)   {   input[0] = '\0';   return input;   }   while(1)   {   input[i] = (char)fgetc(fp);   if (i == size)   {   input[i+1] = '\0';   return input;   }   										  --len;   if (feof(fp) || (!(len)))   {   i++;   input[i] = '\0';   return input;   }   i++;   }   }   return NULL;  
}	void unencode_for_name_pass(char *input)
{int i = 0;   int j = 0;   // 我们获取的input字符串可能像如下的形式   // Username="admin"&Password="aaaaa"   // 其中"Username="和"&Password="都是固定的   // 而"admin"和"aaaaa"都是变化的,也是我们要获取的   // 前面9个字符是UserName=   // 在"UserName="和"&"之间的是我们要取出来的用户名   	for ( i = 9; i < (int)strlen(input); i++ )  {   if (input[i] == '&')   {   name[j] = '\0';   break;   }                                       name[j++] = input[i];   }   // 前面9个字符 + "&Password="10个字符 + Username的字符数   // 是我们不要的,故省略掉,不拷贝   for ( i = 19 + strlen(name), j = 0; i < (int)strlen(input); i++ ){   pass[j++] = input[i];   }   pass[j] = '\0';   	//printf("Your Username is %s<br> Your Password is %s<br> \n", name, pass);   printf("Content-type: text/html\n\n");   //告诉编译器,用html语法来解析if((strcmp(name,"Romeo") == 0)&&(strcmp(pass,"123") == 0))		//html登陆的用户名和密码{printf("<script language='javascript'>document.location = 'http://192.168.1.100/choose.html'</script>"); //自动跳转到这个页面	}else{printf("用户名或密码错误<br><br>");  //exit(-1);}
}int main()   
{   char *input;   char *req_method;   printf("Content-type: text/html\n\n");   //告诉编译器,用html语法来解析printf("The following is query reuslt:<br><br>");   req_method = getenv("REQUEST_METHOD");   input = getcgidata(stdin, req_method);   //获取URL 编码的数据unencode_for_name_pass(input);   //解码,并判断用户名,密码,如果正确,跳转至选择界面,否则提示错误	return 0;   
}   

上述代码就能够实现自动解析用户名和密码并且实现在登陆成功后跳转至二级页面的操作;
二级页面如下:
在这里插入图片描述
三级页面如下:
在这里插入图片描述
四级页面如下:
在这里插入图片描述
到这里呢我们就可以开始点灯了,但是我们必须清楚,网页下发指令后BOA服务器和CGI究竟是如何进行相应的;下来看一下cgi的工程框架:
在这里插入图片描述

下面看一下这个是a9_led.c的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 8struct 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("led",buf,N);				//bled网页键值进行接收,接收8个字节cgiFormString("store",sto_no,2);if((key = ftok("/tmp", 'g')) < 0)		//创建IPC对象键值(生成一个IPC对象),用于消息队列,参数1是指定的文件名,参数2是子序号{perror("ftok");exit(1);}if((msgid = msgget(key, 0666)) < 0)		//创建一个消息队列{perror("msgget");exit(1);}bzero (msg_buf.text, sizeof (msg_buf.text));//如果是打开的按钮if (buf[0] == '1'){//开灯msg_buf.text[0] = ((sto_no[0] - 48)) << 6 | (0x0 << 4) | (1 << 0);}else{msg_buf.text[0] = ((sto_no[0] - 48)) << 6 | (0x0 << 4) | (0 << 0);}msg_buf.type = 1L;		//1L表示home1,也表示消息队列的消息类型msg_buf.msgtype = 1L;	//表示led设备的消息类型//向消息队列中写入消息,将消息发出,在A9端进行接收//这里发送的字节数为sizeof(msg_buf)-sizeof(long)的原因是由于不需要传送消息类型,只需要传送具体的消息大小即可(也就是msg结构体的第一个变量不需要作为发送的有效字节传送)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; 
} 

代码中注释的消息类型如下,这个是在通信结构体设计的分享中对应用层进程间通信使用消息队列机制的具体实现;
在这里插入图片描述
那么当cgi发把消息写入到消息队列后,那应用层究竟是如何进行接收的呢,继续来看吧:
下图是应用层的框架:
在这里插入图片描述
在pthread_client_request.c中,对消息队列进行了接收操作:

#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,没有则创建并返回创建消息队列的idif(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;}}}
#endif 

下面来看一下led线程里都干了什么事情呢,代码如下:

#include "data_global.h"
#include "chrdev.h"
#include <unistd.h>//led的锁资源和条件变量,用来进行同步和互斥操作
extern pthread_mutex_t  	mutex_led;
extern pthread_cond_t      	cond_led;extern unsigned char cmd_seg;//流水灯
int fswaterled_control(int led_fd, int times);
//四位二进制表示十六进制
int fsled_control(int led_fd, unsigned char led_control_cmd);  //发送的数字
//关闭所有的灯
int fsled_close_all(int led_fd);//:A9LED模块线程.
void *pthread_led(void *arg)
{printf("pthread_led\n");int i, j;int led_fd;led_desc_t led;led_fd = open(LED_DEV, O_RDWR);if(led_fd == -1){printf("open failed.\n");}printf("led_fd ;%d.\n",led_fd);while(1){pthread_mutex_lock(&mutex_led); 				//锁和条件变量的操作都是原子操作,不会被cpu的任务调度机制打断printf("led ioctl:***********\n");pthread_cond_wait(&cond_led,&mutex_led); 		//会主动释放上面的锁并且判断是否有唤醒信号,在被唤醒后主动又继续加上锁(wait前必须加锁)printf("led ioctl:***********\n");if(cmd_led == 0x41){fswaterled_control(led_fd, 2);cmd_led = 0;}int tmp = cmd_seg & 0xf0;if(!(tmp ^ 0x70)) {fsled_control(led_fd, cmd_seg);}pthread_mutex_unlock(&mutex_led);}close(led_fd);}int fsled_control(int led_fd, unsigned char led_control_cmd)
{int i = 0;led_desc_t led;led_control_cmd &= 0x0f;int shift_count = 1; //第0位,第1 - 3位 printf("led_control_cmd = %d.\n",led_control_cmd);fsled_close_all(led_fd);sleep(3);while(led_control_cmd){if(shift_count >= 5)break;if((led_control_cmd & 0x1) == 1){ //第0位开始 = LED2shift_count ++;  // = 2  LED2 printf("if shift_count :%d.\n",shift_count);led.which = shift_count; //led2 3 4 5 灯ioctl(led_fd,FSLEDON,&led);usleep(50000);  //让驱动响应的时间}else {shift_count ++;printf("else shift_count :%d.\n",shift_count);led.which = shift_count; //led2 3 4 5 灯ioctl(led_fd,FSLEDOFF,&led);usleep(50000);}led_control_cmd >>= 1;}return 0;
}int fsled_close_all(int led_fd)
{int i = 0;led_desc_t led;	for(i = 2;i < 6;i ++){led.which = i;ioctl(led_fd,FSLEDOFF,&led);usleep(50000);}return 0;
}int fswaterled_control(int led_fd, int times)
{int i = 0,j = 0;led_desc_t led;	for(j = 0;j < times;j ++){for(i = 2;i < 6;i ++){led.which = i;ioctl(led_fd,FSLEDON,&led);usleep(500000);led.which = i;ioctl(led_fd,FSLEDOFF,&led);usleep(500000);}}return 0;
}

当然这里不要忘记在跑主框架之前先要加载led的驱动模块哦,要不然我们前面写的再牛,这里的灯也是亮不起来的,哈哈哈哈!


总结

好啦,本期的分享大概就到这里结束了,是不是点个灯这个操作还是具有一定的难度的呢,虽然步骤过程很多,但是每一步都需要我们谨小慎微,把每一步做好,最后自然就能把灯点亮;不要着急,我饿能够按照步骤整理出来是因为我整个项目已经做完了,所以大家不要着急,还是需要一步步来,后面做完后大家肯定比我理解的更深刻!把灯点亮后,那后面蜂鸣器就不是什么问题了,都是一样的原理,是不是跃跃欲试了!加油哦!最后,各位小伙伴们如果有收获,可以点赞收藏哦,你们的认可是我创作的动力,一起加油!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java数据结构重点知识】第一节:认识数据结构与算法、集合框架

一&#xff1a;数据结构与算法 1.数据结构 数据结构是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素的集合 2.算法 算法就是定义良好的计算过程。他取一个或一组的值为输入&#xff0c;并产生一个或一组作为输出。简单来说就是一系列的…

【递归、搜索与回溯算法】第七节.257. 二叉树的所有路径和46. 全排列

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;递归、搜索与回溯算法 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&am…

《动手深度学习》线性回归简洁实现实例

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

antv/g6使用教程及图配置

介绍 G6 是一款由蚂蚁金服 AntV 团队开发的 JavaScript 图形引擎&#xff0c;用于构建各种交互式可视化图形&#xff0c;包括但不限于图表、网络拓扑图、关系图、流程图等。无论是数据分析、决策支持&#xff0c;还是信息可视化&#xff0c;G6 都是一个强大的工具。 以下是 G…

蓝牙 - BLE SPP实现举例 (Bluecode Protocol Stack)

这里以一个无线扫描枪设备为例&#xff0c;这个设备会通过蓝牙通讯协议连接一个底座&#xff0c;使用的是BLE SPP进行通讯。 扫描枪用来扫条码&#xff0c;解析出条码信息后&#xff0c;将数据通过无线传输给底座&#xff0c;底座再通过USB将数据传送给电脑。 底座是Central d…

一篇博客理解Recyclerview的使用

从Android 5.0开始&#xff0c;谷歌公司推出了RecylerView控件&#xff0c;当看到RecylerView这个新控件的时候,大部分人会首先发出一个疑问&#xff0c;recylerview是什么&#xff1f;为什么会有recylerview也就是说recylerview的优点是什么&#xff1f;recylerview怎么用&…

C#,数值计算——分类与推理Svmpolykernel的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public class Svmpolykernel : Svmgenkernel { public int n { get; set; } public double a { get; set; } public double b { get; set; } public double d { get; set; …

故障诊断模型 | Maltab实现LSTM长短期记忆神经网络故障诊断

文章目录 效果一览文章概述模型描述源码设计参考资料效果一览 文章概述 故障诊断模型 | Maltab实现LSTM长短期记忆神经网络故障诊断 模型描述 长短记忆神经网络——通常称作LSTM,是一种特殊的RNN,能够学习长的依赖关系。 他们由Hochreiter&Schmidhuber引入,并被许多人进行了…

美妆造型教培服务预约小程序的作用是什么

美业市场规模很高&#xff0c;细分类目更是比较广&#xff0c;而美妆造型就是其中的一类&#xff0c;从业者也比较多&#xff0c;除了学校科目外&#xff0c;美妆造型教培机构也有生意。 对机构来说主要目的是拓客引流-转化及赋能&#xff0c;而想要完善路径却是不太容易&…

机器人的触发条件有什么区别,如何巧妙的使用

简介​ 维格机器人触发条件,分为3个,分别是: 有新表单提交时、有记录满足条件时、有新的记录创建时 。 看似3个,其实是能够满足我们非常多的使用场景。 本篇将先介绍3个条件的触发条件,然后再列举一些复杂的触发条件如何用现有的触发条件来满足 注意: 维格机器人所有的…

剖析C语言中的自定义类型(结构体、枚举常量、联合)兼内存对齐与位段

目录 前言 一、结构体 1. 基本定义与使用 2. 内存对齐 3. 自定义对齐数 4. 函数传参 二、位段 三、枚举 四、联合&#xff08;共同体&#xff09; 总结​​​​​​​ 前言 本篇博客将介绍C语言中的结构体&#xff08;struct&#xff09;、枚举&#xff08;enum&…

【Redis】高并发分布式结构服务器

文章目录 服务端高并发分布式结构名词基本概念评价指标1.单机架构缺点 2.应用数据分离架构应用服务集群架构读写分离/主从分离架构引入缓存-冷热分离架构分库分表&#xff08;垂直分库&#xff09;业务拆分⸺微服务 总结 服务端高并发分布式结构 名词基本概念 应⽤&#xff0…

【错误解决方案】ModuleNotFoundError: No module named ‘ngboost‘

1. 错误提示 在python程序&#xff0c;尝试导入一个名为ngboost的模块&#xff0c;但Python提示找不到这个模块。 错误提示&#xff1a;ModuleNotFoundError: No module named ‘ngboost‘ 2. 解决方案 出现上述问题&#xff0c;可能是因为你还没有安装这个模块&#xff0c;…

了解Docker的文件系统网络模式的基本原理

Docker文件系统 Linux基础 一个Linux系统运行需要两个文件系统&#xff1a; bootfs rbootfs bootfs&#xff08;boot file system&#xff09; bootfs 即引导文件系统&#xff0c;Linux内核启动时使用的文件系统。对于同样的内核版本的不同Lunx发行版本&#xff0c;其boot…

百度富文本上传图片后样式崩塌

&#x1f525;博客主页&#xff1a; 破浪前进 &#x1f516;系列专栏&#xff1a; Vue、React、PHP ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 问题描述&#xff1a;上传图片后&#xff0c;图片会变得很大&#xff0c;当点击的时候更是会顶开整个的容器的高跟宽 原因&#…

C++之类型转换

目录 一、C语言中的类型转换 二、C的强制类型转换 1、 static_cast 2、reinterpret_cast 3、 const_cast 4、dynamic_cast 一、C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者返回值类型…

idea的设置

1.设置搜索encoding,所有编码都给换为utf-8 安装插件 eval-reset插件 https://www.yuque.com/huanlema-pjnah/okuh3c/lvaoxt#m1pdA 设置活动模板,idea有两种方式集成tomcat,一种是右上角config配置本地tomcat,一种是插件,如果使用插件集成,则在maven,pom.xml里面加上tomcat…

openGauss学习笔记-110 openGauss 数据库管理-管理用户及权限-Schema

文章目录 openGauss学习笔记-110 openGauss 数据库管理-管理用户及权限-Schema110.1 创建、修改和删除Schema110.2 搜索路径 openGauss学习笔记-110 openGauss 数据库管理-管理用户及权限-Schema Schema又称作模式。通过管理Schema&#xff0c;允许多个用户使用同一数据库而不…

XML教学视频(黑马程序员精讲 XML 知识!)笔记

第一章XML概述 1.1认识XML XML数据格式&#xff1a; 不是html但又和html有点相似 XML数据格式最主要的功能就是数据传输&#xff08;一个服务器到另一个服务器&#xff0c;一个网站到另一个网站&#xff09;配置文件、储存数据当做小型数据可使用、规范数据格式让数据具有结…

多线程---synchronized特性+原理

文章目录 synchronized特性synchronized原理锁升级/锁膨胀锁消除锁粗化 synchronized特性 互斥 当某个线程执行到某个对象的synchronized中时&#xff0c;其他线程如果也执行到同一个对象的synchronized就会阻塞等待。 进入synchronized修饰的代码块相当于加锁 退出synchronize…