目录
一、模块化编程思维
二、安防监控项目主框架搭建
其实我们以前学习32使用keil的时候就是再用模块化的思维。每个硬件都单独有一个实现功能的C文件和声明函数,进行宏定义以及引用需要使用头文件的h文件。
比如简单的加减乘除取余操作我们把他们每个都封装一个文件
但是他们每个文件里功能很少所以可以共用一个h文件common
这是我们的整个架构的代码
buzzer和led都是板子上的外设,
transfer:是进行数据转换的
sqlite就是数据库之前学过的
refresh是刷新,我们上传的数据需要刷新到网页上
client request是客户端请求,从上往下发来的命令都由我们主机上的服务器处理
sms就是GPRS线程的程序可以打电话和发短信
#include
#include #include #include #include #include #include #include #include "data_global.h" void release_pthread_resource(int signo); extern pthread_mutex_t mutex_client_request, mutex_refresh, mutex_sqlite, mutex_transfer, mutex_sms, mutex_buzzer, mutex_led; extern pthread_cond_t cond_client_request, cond_refresh, cond_sqlite, cond_transfer, cond_transfer, cond_sms, cond_buzzer, cond_led; extern int msgid; extern int shmid; extern int semid; pthread_t id_client_request, id_refresh, id_sqlite, id_transfer, id_sms, id_buzzer, id_led; int main(int argc, const char *argv[]) { // pthread_mutex_init(&mutex_client_request,NULL); pthread_mutex_init(&mutex_refresh,NULL); pthread_mutex_init(&mutex_sqlite,NULL); pthread_mutex_init(&mutex_transfer,NULL); pthread_mutex_init(&mutex_sms,NULL); pthread_mutex_init(&mutex_buzzer,NULL); pthread_mutex_init(&mutex_led,NULL); signal (SIGINT, release_pthread_resource); pthread_cond_init(&cond_client_request,NULL); pthread_cond_init(&cond_refresh,NULL); pthread_cond_init(&cond_sqlite,NULL); pthread_cond_init(&cond_transfer,NULL); pthread_cond_init(&cond_sms,NULL); pthread_cond_init(&cond_buzzer,NULL); pthread_cond_init(&cond_led,NULL); pthread_create(&id_client_request, NULL,pthread_client_request,NULL); pthread_create(&id_refresh, NULL,pthread_refresh,NULL); pthread_create(&id_sqlite, NULL,pthread_sqlite,NULL); pthread_create(&id_transfer, NULL,pthread_transfer,NULL); pthread_create(&id_sms, NULL,pthread_sms,NULL); pthread_create(&id_buzzer, NULL,pthread_buzzer,NULL); pthread_create(&id_led, NULL,pthread_led,NULL); // pthread_join(id_client_request,NULL); printf ("pthread1\n"); pthread_join(id_refresh,NULL); printf ("pthread2\n"); pthread_join(id_sqlite,NULL); printf ("pthread3\n"); pthread_join(id_transfer,NULL); printf ("pthread4\n"); pthread_join(id_sms,NULL); printf ("pthread5\n"); pthread_join(id_buzzer,NULL); printf ("pthread6\n"); pthread_join(id_led,NULL); printf ("pthread7\n"); return 0; } void release_pthread_resource(int signo) { pthread_mutex_destroy (&mutex_client_request); pthread_mutex_destroy (&mutex_refresh); pthread_mutex_destroy (&mutex_sqlite); pthread_mutex_destroy (&mutex_transfer); pthread_mutex_destroy (&mutex_sms); pthread_mutex_destroy (&mutex_buzzer); pthread_mutex_destroy (&mutex_led); pthread_cond_destroy (&cond_client_request); pthread_cond_destroy (&cond_refresh); pthread_cond_destroy (&cond_sqlite); pthread_cond_destroy (&cond_transfer); pthread_cond_destroy (&cond_sms); pthread_cond_destroy (&cond_buzzer); pthread_cond_destroy (&cond_led); pthread_detach(id_client_request); pthread_detach(id_refresh); pthread_detach(id_sqlite); pthread_detach(id_transfer); pthread_detach(id_sms); pthread_detach(id_buzzer); pthread_detach(id_led); printf("all pthread is detached\n"); msgctl (msgid, IPC_RMID, NULL); shmctl (shmid, IPC_RMID, NULL); semctl (semid, 1, IPC_RMID, NULL); exit(0); } 这是主函数,主函数其实就是一个进程,它创建了一堆线程来实现这些功能。
#include "data_global.h" //½ԊԚigBeeµŊɼ¯µÁ9ƽ̨µĴ«¸ц뽾 void *pthread_transfer(void *arg) { printf("pthread_analysis\n"); }
transfer里面就是打印了一句话具体的功能还都没有实现。
#include "data_global.h"
//:A9LED模块线程.
void *pthread_led(void *arg)
{
printf("pthread_led\n");
#if 0
5. open(dev_led, )
6. pthread_cond_wait (cond_led, );
7. 获取dev_led_mask(控制标志)
8. 通过ioctl()控制led
#endif
}
#include "data_global.h"
//:A9·儹Ƿ¿ٖǏ߳ʮ
void *pthread_buzzer(void *arg)
{
printf("pthread_buzzer\n");
#if 0
1. open(dev_buzzer, )
2. pthread_cond_wait (cond_buzzer, );
3. »ev_buzzer_mask(¿ٖƱ떾)
4. ͨ¹£¨£©¿ٖŢuzzer
#endif
}
#include "data_global.h"
extern int msgid;
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 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);
if(msgid == -1) {
if(errno == EEXIST){
msgid = msgget(key,0777);
}else{
perror("fail to msgget");
exit(1);
}
}
printf("pthread_client_request\n");
while(1){
bzero(&msgbuf,sizeof(msgbuf));
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:
printf("hello led\n");
break;
case 2L:
printf("hello beep\n");
break;
case 3L:
printf("hello seg\n");
break;
case 4L:
printf("hello fan\n");
break;
case 5L:
printf("set env data\n");
printf("temMAX: %d\n",*((int *)&msgbuf.text[1]));
printf("temMIN: %d\n",*((int *)&msgbuf.text[5]));
printf("humMAX: %d\n",*((int *)&msgbuf.text[9]));
printf("humMAX: %d\n",*((int *)&msgbuf.text[13]));
printf("illMAX: %d\n",*((int *)&msgbuf.text[17]));
printf("illMAX: %d\n",*((int *)&msgbuf.text[21]));
break;
case 6L:
case 7L:
case 8L:
case 9L:
printf("ţ¿ʒԽ«֢ЩطΪ)չ4ѧϰ£¬¼ԓˮ\n");
break;
case 10L:
{
int i = 0 , j = 0 ;
for(i = 0 ; i < 11; i++){
recive_phone[i] = msgbuf.text[i];
}
recive_phone[i] = '\0';
printf("recive:%s\n",recive_phone);
for(j = 0 ;msgbuf.text[i] != '\0' && j < 12; i++, j++)
{
center_phone[j] = msgbuf.text[i];
}
center_phone[j] = '\0';
printf("center:%s\n",center_phone);
#if 0
pthread_mutex_lock (&mutex_slinklist);
sqlite_InsertLinknode (ENV_UPDATE, all_info_RT, sto_no, 0);//0,0分别是仓库号和货物种类号
pthread_mutex_unlock (&mutex_slinklist);
pthread_cond_signal (&cond_sqlite);
#endif
}
break;
default:
break;
}
}
}
#if 0
long msgtype;//¾ࠌ嶄лϢ`э
лϢ`эµķׅ䣺
1L: LED¿ٖ
2L: ·儹Ƿ¿ٖ
3L: ̄·LEDµDŽ£ŢµŊ
4L: ·芈
5L: ςʪ¶ɗʨ׃
6L-7L-8L-9L,ԃԚ¸ŀ©չ
10L: 3Gͨхģ¿笇PRS
switch(msgbuf.msgtype){
case 1L: ... break;
....
default .... break;
}
#endif
这些线程都还没有具体实现功能这就是一个框架
两种意识:
1、分层意识
2、数据流
*****************************************************
分层分析:
##################
web网页端显示部分:
环境信息 === 实时刷新环境数据
摄像头采集图像 === 采集监控信息
硬件控制 === 下发要去控制的命令
A9数据处理部分
创建进程、线程
每条线程做自己的事情
涉及到进程间通信
数据处理===>分发(上行数据 or 下行数据)
A9-ZigBee数据采集部分
A9采集部分
ZigBee采集部分
(STM32平台(可以自己扩展))
*****************************************************
数据流分析:
##################
数据上传:
数据下发:
制定通信的协议(结构体):
数据要怎么上传,上传的目的是为了什么?
数据要怎么下发,下发的目的又是为了什么?
数据的上传: ====> 共享内存上传数据 ====> 显示并交给用户查看环境信息
数据的下发用于控制硬件:====> 消息队列下发数据 ===> 控制硬件改变环境
**************************************************************************
分层分析:
web网页端显示部分:
环境信息: adc电压数据
mpu6050的六轴数据
温度
湿度
摄像头采集图像:
硬件控制: 风扇
LED灯
蜂鸣器
GPRS ==== 发短信或打电话
A9数据处理部分
数据流向分析:
1、ZigBee(采集终端)-->A9(处理平台)
2、A9(处理平台)-->网页(显示平台)
3、网页(显示平台)-->A9(处理平台)
4、A9(处理平台)--->ZigBee(采集终端)
A9-ZigBee采集部分
外设驱动 --------在应用层去获取外设的状态或数据
A9--------- 蜂鸣器 ------------------蜂鸣器报警
LED灯 ------------------卧室-厕所-楼道-公共照明 --------LED2-LED3-LED4-LED5
按键 ------------------按键触发中断---控制卧室和厕所灯-----LED2-LED3
ADC -----------------获取ADC的采样数据
mpu6050 ------------------获取MPU6050的六轴数据
zigbee------adc ------主---协调器
风扇 ------从---终端节点 下发命令控制风扇
温湿度 ------从---终端节点 上传温湿度数据
(光敏)
小结:
| | | |
| ZigBee | A9 | web |
| | | |
| adc | 蜂鸣器 | 环境信息:-----------------adc电压数据
| 风扇 | LED灯 | 摄像头采集:-----usb摄像头 mpu6050的六轴数据
| 温湿度 | 按键 | 硬件控制: |------风扇 温度
| (光敏) | ADC | | LED灯 湿度
mpu6050 蜂鸣器
四路led灯模拟数码管 GPRS
四路led灯模拟数码管
数据流分析:
数据上传:
ZigBee |
温湿度数据 |
A9 |
ADC采集 |-----------上传这些数据
加速计数据 |
陀螺仪数据 |
摄像头 |
视频流图像 |
数据下发:
ZigBee: |
风扇 |
A9: |
蜂鸣器 |-----打开设备节点控制硬件
LED灯 |
四路LED灯模拟的数码管 |
GPRS: |
3G通信模块 |
#define GPRS_DEV "/dev/ttyUSB0"
#define ZIGBEE_DEV "/dev/ttyUSB1"
#define BEEPER_DEV "/dev/fsbeeper0"
#define LED_DEV "/dev/fsled0"
制定通信的结构体:
数据的上传:
数据类型定义:
typedef uint8_t unsigned char; =======参考:
typedef uint16_t unsigned short;
typedef uint32_t unsigned int;
//考虑到内存对齐的问题
struct makeru_zigbee_info{
uint8_t head[3]; //标识位: 'm' 's' 'm' makeru-security-monitor
uint8_t type; //数据类型 'z'---zigbee 'a'---a9
------------->crc ...加密算法 <--------------
float temperature; //温度
float humidity; //湿度
float tempMIN;//温度下限
float tempMAX;//温度上限
float humidityMIN; //湿度下限
float humidityMAX; //湿度上限
uint32_t reserved[2]; //保留扩展位,默认填充0
//void *data; 内核预留的扩展接口 参考版
};
struct makeru_a9_info{
uint8_t head[3]; //标识位: 'm' 's' 'm' makeru-security-monitor
uint8_t type; //数据类型 'z'---zigbee 'a'---a9
uint32_t adc;
short gyrox; //陀螺仪数据
short gyroy;
short gyroz;
short aacx; //加速计数据
short aacy;
short aacz;
uint32_t reserved[2]; //保留扩展位,默认填充0
//void *data; 内核预留的扩展接口 参考版
};
struct makeru_env_data{
struct makeru_a9_info a9_info;
struct makeru_zigbee_info zigbee_info;
};
//所有监控区域的信息结构体
struct env_info_client_addr
{
struct makeru_env_data monitor_no[MONITOR_NUM]; //数组 老家---新家
};
数据的下发:(采用消息队列的方式下发数据到下位机上)
数据的下发用于控制硬件:
man msgsnd
#include
#include
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
消息队列用于通信的结构体: 包括数据类型和数据
将消息队列封装成函数,直接通过参数传递的方式来发送信息:
int send_msg_queue(long type,unsigned char text)
{
struct msg msgbuf;
msgbuf.type = 1L;
msgbuf.msgtype = type; //具体的消息类型
msgbuf.text[0] = text; //控制命令字
if(msgsnd(msgid,&msgbuf,sizeof(msgbuf) - sizeof(long),0) == -1){
perror("fail to msgsnd type2");
exit(1);
}
return 0;
}
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
//消息队列结构体
#define QUEUE_MSG_LEN 32
struct msg
{
long type; //从消息队列接收消息时用于判断的消息类型 ==== 暂时不用 1L===home1 2L===home2 ...
long msgtype;//具体的消息类型 === 指代控制的设备,是什么类型的设备
unsigned char text[QUEUE_MSG_LEN];//消息正文 ====> CMD 控制指定的设备
};
long msgtype;//具体的消息类型
消息类型的分配:
1L: LED控制
2L: 蜂鸣器控制
3L: 四路LED灯模拟的数码管
4L: 风扇
5L: 温湿度最值设置
6L-7L-8L-9L,用于个人的扩展
10L: 3G通信模块-GPRS
switch(msgbuf.msgtype){
case 1L: ... break;
....
default .... break;
}
控制命令的制定:
消息队列接收消息:
msgrcv (msgid, &msgbuf, sizeof (msgbuf) - sizeof (long), 1L, 0);
解析buf中的数据:
printf ("Get %ldL msg\n", msgbuf.msgtype);
printf ("text[0] = %#x\n", msgbuf.text[0]);
A9-ZIGBEE通用指令
命令格式:一个字节,unsigned char 对应消息队列中正文的类型:
unsigned int
8位
----------------------------------------
7 6 | 5 4 | 3 2 1 0
平台编号| 设备编号 | 操作设备
----------------------------------------
0 0
0 1
1 0
1 1
平台编号
0x00 0号-ZigBee平台
0x40 1号-A9/A53平台
0x80 2号-STM32平台(可以自己扩展)
0xc0 3号-avr arduino....保留(如果平台继续增多的话可以采用2个字节或多个字节来对设备进行
唯一的编号,比如A9类下的1号平台,2号平台,先分类,然后再具体标识设备)
----------------------------------------
设备编号 操作掩码
0x00 LED 0x00 全部关闭
0x01 全部打开
0x02 打开LED2
0x03 打开LED3
0X04 打开LED4
0x05 打开LED5
0X10 打开流水灯
----------------------------------------
0x10 蜂鸣器 0x00 关闭
0x01 打开
0x02 自动报警关闭
0x03 自动报警打开
----------------------------------------
0x20 风扇 0x00 关闭风扇
0x01 打开风扇
----------------------------------------
0x30 数码管 0x0~0xF 显示0~F数字(四盏灯,对应0000-表示0,0001-表示1....1110-表示14)
0x0f 关闭数码管 led2-3-4-5
----------------------------------------
控制命令:
平台编号 + 设备编号 + 操作掩码 = 命令 (命令的封装)
例如:
0x00 + 0x20 + 0x01 = 0x21 风扇打开
0x40 + 0x10 + 0x01 = 0x51 蜂鸣器打开
0x40 + 0x30 + 0x08 = 0x78 数码管显示8
0x40 + 0x30 + 0x0f = 0x7f 关闭数码管
a 高位数据,b代表低位数据
short c
unsigned char a ,b;
c = a | b;
c = a + b;
上行:封装的结构体====共享内存和信号量 ===>交给CGI(C语言和HTML语言之间的转化接口)===>交给HTML
下行:封装的命令字====消息队列 ====>msgbuf msgsnd===>控制命令字封装在msgsnd的msgbuf中 ===>A9端解析==>向下控制硬件
这是之前定义好的结构体,id和key值,一定要严格按照定好的来,不然就会造成无法通信的后果,再团队合作中有想法要在做前提,这样可以提前修改。
现在线程都启动成功了
CROSS_COMPILE=arm-linux-
CC=$(CROSS_COMPILE)gcc
CFLAGS= -c -g
#LDFLAGS= -lpthread -L ./lib -lsqlite3
LDFLAGS= -lpthreadOBJS=main.o data_global.o pthread_transfer.o \
pthread_client_request.o pthread_buzzer.o pthread_led.o\
pthread_sqlite.o \
pthread_refresh.o pthread_sms.omonitor_obj :$(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
mv *o ./obj
$(OBJS):%.o:%.c
$(CC) $(CFLAGS) $< -o $@install:
sudo cp monitor_obj ~/nfs_rootfs/.PHONY:clean
clean:
rm *.o monitor_obj -rf
上面是makefile的内容