前言
本节实现网页下发控制命令至A7端的功能。A7线程通过消息队列与CGI进程进行通信,获取网页下发的控制命令,并执行对应硬件设备的控制操作。具体数据流向如下图所示,本次实验重点实现网页到A7端的下行数据通路。 
硬件控制界面
目前网页支持的控制项包括LED、蜂鸣器、数码管和风扇。用户选择其中一项设备并设定其状态后提交,即可生成对应的控制命令下发至A7端。
控制命令协议帧
我们以LED控制命令的下发为例,在通信协议中我们规定如下通信协议帧。
| 字段含义 | 平台编号 | 平台编号 | 设备编号 | 设备编号 | 操作编码 | 操作编码 | 操作编码 | 操作编码 |
平台编号:
| 0号平台 | 0x00 | ZigBee 平台 |
| 1号平台 | 0x40 | ARM 平台 |
| 2号平台 | 0x80 | STM32 平台 |
| 3号平台 | 0xC0 | 保留扩展 |
LED 设备(设备编号:0x00)
操作编码:
| 0x00 | 0x00 | 全部关闭 |
| 0x01 | 0x01 | 全部打开 |
| 0x02 | 0x02 | 打开 LED2 |
| 0x03 | 0x03 | 打开 LED3 |
| 0x04 | 0x04 | 打开 LED4 |
| 0x05 | 0x05 | 打开 LED5 |
| 0x10 | 0x10 | 打开流水灯 |
LED设备位于ARM板,平台编号为0x40,设备编号为0x00。因此,开启指令码为0x40 + 0x00 + 0x00 = 0x40,关闭指令码为0x40 + 0x00 + 0x01 = 0x41。其余设备命令码类似,不再赘述,本次测试先以LED的开启和关闭命令进行验证。
CGI程序
点击提交按钮后,网页通过BOA服务器向CGI程序发送表单数据,我们在HTML中标定了三个键值对作为表单数据。由action可知,表单数据提交到a7_led.cgi中。
键值对解释如下:
| led | LED控制 | 1 | 亮 |
| led | LED控制 | 0 | 灭 |
| sto_no | 仓库编号 | 1 | 固定值,用于标识 |
以GET方式提交时,URL会携带用户选择的值:
选择"亮":/cgi-bin/a7_led.cgi?led=1&sto_no=1 选择"灭":/cgi-bin/a7_led.cgi?led=0&sto_no=1
在 a7_led.c 中,通过 CGIC 库提供的函数即可获取表单提交的键值对数据。其中,cgiFormString 函数用于获取字符串类型的表单字段。
char led[2];
char sto_no[2]; // 平台编号
cgiFormString("led", led, sizeof(led)); // 获取表单数据
cgiFormString("sto_no", sto_no, sizeof(sto_no)); // 获取表单数据
获得LED值数据后,在程序中对控制指令进行拼接。
if (led[0] == '1')
{
msg_buf.mtext[0] = ((atoi(sto_no) << 6) | (0x00 << 4) | (0x01));//0X41
}
else if (led[0] == '0')
{
msg_buf.mtext[0] = ((atoi(sto_no) << 6) | (0x00 << 4) | (0x00));//0X40
}
获取控制命令后,需将其发送至A7应用程序,由A7根据命令执行相应硬件操作。由于CGI与A7属于不同进程,需借助进程间通信机制,此处采用消息队列实现数据传递。
此前已定义好消息队列的结构体格式,具体如下:
struct msg{
long mtype;//消息队列接受时的消息类型
long msgtype;//具体的消息类型 1L 2L 3L…
unsigned char mtext[QUEUE_SIZE];
};
其中mtype始终设置为1,我们根据msgtype来判断设备类型。 对消息队列内容填充完毕后,调用 msgsnd 函数发送命令。完整的 a7_led.c 实现如下:
#include "cgic.h"
#include "data_global.h"
int cgiMain()
{
char led[2];
char sto_no[2]; // 平台编号
struct msg msg_buf; // 定义消息结构体变量
key = ftok("/tmp", 'i'); // 生成消息队列的键值
int msgid = msgget(key, IPC_CREAT | 0666); // 创建消息队列
if (msgid == –1)
{
cgiHeaderContentType("text/html");
fprintf(cgiOut, "msgget error!");
return –1;
}
bzero(msg_buf.mtext, sizeof(msg_buf.mtext)); // 清空消息文本内容
cgiFormString("led", led, sizeof(led)); // 获取表单数据
cgiFormString("sto_no", sto_no, sizeof(sto_no)); // 获取表单数据
if (led[0] == '1')
{
msg_buf.mtext[0] = ((atoi(sto_no) << 6) | (0x00 << 4) | (0x01));
}
else if (led[0] == '0')
{
msg_buf.mtext[0] = ((atoi(sto_no) << 6) | (0x00 << 4) | (0x00));
}
msg_buf.mtype = 1; // 设置消息类型
msg_buf.msgtype = 1L; // 设置具体的消息类型:灯
msgsnd(msgid, &msg_buf, sizeof(msg_buf)–4, 0); // 发送消息到消息队列
cgiHeaderContentType("text/html");//输出HTTP响应头,告诉浏览器返回的内容是HTML格式。
fprintf(cgiOut, "LED %s!", led);
return 0;
}
A7程序
在A7端的数据请求线程 pthread_request.c 中,我们创建消息队列,所使用的键值需与CGI程序保持一致。线程进入 while 循环后,持续阻塞等待消息队列中的数据。msgrcv 函数默认以阻塞方式运行,一旦接收到某一设备发来的命令,即通过 switch 语句对 msgtype 进行判断,从而调用对应的处理线程执行具体的硬件控制操作。 代码如下:
#include "data_global.h"
struct msg msg_request;
void *pthread_request(void *arg)
{
key = ftok("/tmp", 'i');
if (key < 0)
{
perror("ftok failed!");
exit(1);
}
int msgid = msgget(key, IPC_CREAT | 0666); // 创建消息队列
if (msgid < 0)
{
perror("msgget failed!");
exit(1);
}
while (1)
{
msgrcv(msgid, &msg_request, sizeof(struct msg) – sizeof(long), 0, 0); // 接收消息
printf("Received message: mtype=%ld, msgtype=%ld, mtext=%s\\n", msg_request.mtype, msg_request.msgtype, msg_request.mtext);
switch (msg_request.msgtype)
{
case 1L:
printf("hello led\\r\\n");
pthread_mutex_lock(&mutex_led);
led_command = msg_request.mtext[0];
led_pending = 1; // 设置LED命令待处理标志
pthread_mutex_unlock(&mutex_led);
pthread_cond_signal(&cond_led); // 发送LED控制条件
break;
case 2L:
printf("hello beep\\r\\n");
pthread_mutex_lock(&mutex_beep);
beep_command = msg_request.mtext[0];
beep_pending = 1; // 设置BEEP命令待处理标志
pthread_mutex_unlock(&mutex_beep);
pthread_cond_signal(&cond_beep); // 发送BEEP控制条件
break;
case 3L:
printf("hello seg\\r\\n");
break;
case 4L:
printf("hello fan\\r\\n");
break;
case 5L:
printf("hello temp_set\\r\\n");
break;
case 6L:
printf("hello reserve\\r\\n");
break;
case 7L:
printf("hello GRPS\\r\\n");
break;
default:
break;
}
}
}
默认情况下,各具体设备的控制线程处于阻塞状态,只有当对应的控制命令到达时,才会唤醒相应线程,将其标志位置1并设置全局命令码,以执行具体的硬件操作。 LED具体线程代码如下:
#include "data_global.h"
int led_pending = 0;
unsigned char led_command = 0;
void set_led(unsigned char command);
void *led_pthread(void *arg)
{
while (1)
{
pthread_mutex_lock(&mutex_led);
// 等待有命令到来
while (led_pending == 0)
{
pthread_cond_wait(&cond_led, &mutex_led);
}
set_led(led_command);// 执行LED控制命令
led_pending = 0; // 重置LED命令待处理标志
pthread_mutex_unlock(&mutex_led);
}
}
// LED控制函数
void set_led(unsigned char command)
{
int fd = open(LED_DEV, O_WRONLY);
if (fd < 0)
{
perror("open LED failed");
return;
}
if (command == 0x41)
{
write(fd, "1", 1);
printf(">>> LED turned ON by led_pthread <<<\\n");
}
else if (command == 0x40)
{
write(fd, "0", 1);
printf(">>> LED turned OFF by led_pthread <<<\\n");
}
close(fd);
}


![[开源]基于STM32单片机WiFi智能停车场系统 Onene车位管理设计 云平台停车场车位检测 APP智慧停车场HK-010-171主机测评](https://www.171host.com/wp-content/uploads/2026/09/20260921150408-6ab1476821e49-220x150.jpg)
