书接上期,我们可以看一下前面的功能设计的部分,网页端的控制还有一个,那就是通过网页来控制zigbee上的风扇节点,这部分的工作量是相当大的,既要实现HTML发送控制命令到A9平台进行接收,又要实现A9平台串口通信控制zigbee协调器,通过zigbee协调器控制zigbee终端节点(这一步是建立在zigbee协调器和终端节点调试好的基础上的);最终呢实现网页控制zigbee节点,这也实现了无线控制!
第一步呢还是先来看命令字是怎样下发的:
#include
#include "cgic.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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, "\n");
fprintf(cgiOut, "My CGI \n");
fprintf(cgiOut, "");
fprintf(cgiOut, "send sucess
");
//fprintf(cgiOut, "返回");
fprintf(cgiOut, "", sto_no[0]);
fprintf(cgiOut, "\n");
fprintf(cgiOut, "\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
#include
#include
#include
#include
#include
#include
#include
#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终端节点硬件了;最后,各位小伙伴们如果有收获,可以点赞收藏哦,你们的认可是我创作的动力,一起加油!