CubeMX配置STM32实现httpd服务器CGI功能并使用网页控制STM32单片机(四)

引言

在前三篇文章中自己介绍了如何配置freeRTOS以及如何配置LWIP,并使用lwip实现一个httpd服务器,使浏览器可以访问。在本章中我们利用CGI功能,实现通过网页来控制单片机的一个LED灯的电平翻转。

自己写的另外五篇文章
从零开始Cubemx配置STM32搭载freeRTOS实现多路ADC(一)
从零开始Cubemx配置STM32搭载freeRTOS以及lwip实现tcp网络通信(二)
从零开始使用CubeMX配置STM32使用lwip实现httpd服务器以及使用vscode编辑阅读keil代码(三)
CubeMX配置STM32实现FatFS文件系统(五)
CUBEMX配置STM32实现FTP文件传输以及使用SNTP获取网络时间并写入RTC

CubeMX配置HTTPD的CGI功能

在这里把CGI功能开启。
在这里插入图片描述
CGI的介绍部分参考这位老哥的文章 https://blog.csdn.net/weixin_39517902/article/details/111205216

CGI是common gateway interface的缩写,大家都译作通用网关接口,但很不幸,我们无法见名知意。

我们知道,web服务器所处理的内容都是静态的,要想处理动态内容,需要依赖于web应用程序,如php、jsp、python、perl等。但是web

server如何将动态的请求传递给这些应用程序?它所依赖的就是cgi协议。没错,是协议,也就是web

server和web应用程序交流时的规范。换句话说,通过cgi协议,再结合已搭建好的web应用程序,就可以让web

server也能"处理"动态请求(或者说,当用户访问某个特定资源时,可以触发执行某个web应用程序来实现特定功能),你肯定知道处理两字为什么要加上双引号。

简单版的cgi工作方式如下:

在这里插入图片描述
例如,在谷歌搜索栏中搜索一个关键词"http",对应的URL为:

https://www.google.com/search?q=http&oq=http&aqs=chrome…69i57j69i60l4j0.1136j0j8&sourceid=chrome&ie=UTF-8

当谷歌的web

server收到该请求后,先分析该url,从中知道了要执行search程序,并且还知道了一系列要传递给search的参数及其对应的value。web

server会将这些程序参数和其它一些环境变量根据cgi协议通过TCP或套接字等方式传递给已启动的cgi程序(可能是cgi进程,或者是已加载的模块cgi模块)。当cgi进程接收到web

server的请求后,调用search程序并执行,同时还会传递参数给search程序。search执行结束后,cgi进程/线程将处理结果返回给web

server,web server再返回给浏览器。

有多种方式可以执行cgi程序,但对http的请求方法来说,只有get和post两种方法允许执行cgi脚本(即上面的search程序)。实际上post方法的内部本质还是get方法,只不过在发送http请求时,get和post方法对url中的参数处理方式不一样而已。

实验过程

在这里我新建一个文件命名为apimain.c

#include "apimain.h"
#include "httpd.h"
#include <stdio.h>

static const char *api_operation_req(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);

//核心就是根据URL的参数来执行不同的处理函数
static const tCGI g_psConfigCGIURIs[] = {   
	{"/img/sics.gif", api_operation_req},
};

const char *api_operation_req(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
{
   // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET);
	HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_3);
    printf("cgi success!!!!\n");
}

void api_server_init(void)
{
    httpd_init();
    //这是CGI的初始化
    http_set_cgi_handlers(g_psConfigCGIURIs, sizeof(g_psConfigCGIURIs)/sizeof(tCGI));

}

应用CGI的核心就是根据URL的参数来执行不同的处理函数,比如请求的URL是http://192.168.0.205/img/sics.gif ,我就是利用了这个GET请求,来实现一个LED灯的电平翻转,刷新网页的时候会执行这个请求。
在这里插入图片描述
CGI具体实现在httpd.c的http_find_file( )函数中,主要是下面几句,可以看到与URL的字符串进行对比,如果一样就进入CGI的这个句柄分支。

#if LWIP_HTTPD_CGI
    http_cgi_paramcount = -1;
    /* Does the base URI we have isolated correspond to a CGI handler? */
    if (httpd_num_cgis && httpd_cgis) {
      for (i = 0; i < httpd_num_cgis; i++) {
        if (strcmp(uri, httpd_cgis[i].pcCGIName) == 0) {
          /*
           * We found a CGI that handles this URI so extract the
           * parameters and call the handler.
           */
          http_cgi_paramcount = extract_uri_parameters(hs, params);
          uri = httpd_cgis[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params,
                                         hs->param_vals);
          break;
        }
      }
    }

刷新网页请求的url是/img/sics.gif,因此我的apimain.c写的就是,这样执行这个请求的时候就会进api_operation_req()这个函数。这样我们就实现了使用网页的请求来控制单片机和其他设备。

static const tCGI g_psConfigCGIURIs[] = {   
	{"/img/sics.gif", api_operation_req},
};

发现的问题

LWIP的HTTPD服务没有实现POST的相关处理,但是httpd.h中已经进行了声明,但是在httpd.c中没有进行具体的实现,这里坑我了一下,我没仔细看,开启LWIP_HTTPD_SUPPORT_POST的宏后,编译不过去,如果想要支持POST操作的话,要自己写函数的实现,主要是这三个函数,网上有具体的实现。

#if LWIP_HTTPD_SUPPORT_POST

/* These functions must be implemented by the application */

/**
 * @ingroup httpd
 * Called when a POST request has been received. The application can decide
 * whether to accept it or not.
 *
 * @param connection Unique connection identifier, valid until httpd_post_end
 *        is called.
 * @param uri The HTTP header URI receiving the POST request.
 * @param http_request The raw HTTP request (the first packet, normally).
 * @param http_request_len Size of 'http_request'.
 * @param content_len Content-Length from HTTP header.
 * @param response_uri Filename of response file, to be filled when denying the
 *        request
 * @param response_uri_len Size of the 'response_uri' buffer.
 * @param post_auto_wnd Set this to 0 to let the callback code handle window
 *        updates by calling 'httpd_post_data_recved' (to throttle rx speed)
 *        default is 1 (httpd handles window updates automatically)
 * @return ERR_OK: Accept the POST request, data may be passed in
 *         another err_t: Deny the POST request, send back 'bad request'.
 */
err_t httpd_post_begin(void *connection, const char *uri, const char *http_request,
                       u16_t http_request_len, int content_len, char *response_uri,
                       u16_t response_uri_len, u8_t *post_auto_wnd);

/**
 * @ingroup httpd
 * Called for each pbuf of data that has been received for a POST.
 * ATTENTION: The application is responsible for freeing the pbufs passed in!
 *
 * @param connection Unique connection identifier.
 * @param p Received data.
 * @return ERR_OK: Data accepted.
 *         another err_t: Data denied, http_post_get_response_uri will be called.
 */
err_t httpd_post_receive_data(void *connection, struct pbuf *p);

/**
 * @ingroup httpd
 * Called when all data is received or when the connection is closed.
 * The application must return the filename/URI of a file to send in response
 * to this POST request. If the response_uri buffer is untouched, a 404
 * response is returned.
 *
 * @param connection Unique connection identifier.
 * @param response_uri Filename of response file, to be filled when denying the request
 * @param response_uri_len Size of the 'response_uri' buffer.
 */
void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len);

#if LWIP_HTTPD_POST_MANUAL_WND
void httpd_post_data_recved(void *connection, u16_t recved_len);
#endif /* LWIP_HTTPD_POST_MANUAL_WND */

#endif /* LWIP_HTTPD_SUPPORT_POST */

总结

到此,通过这四篇文章,我实现了从零使用CUBEMX配置了一个带有FREERTOS以及LWIP和带有CGI功能的HTTPD服务器,以后如果想使用物联网设备开启局域网服务器,就可以使用这个框架。在这里我已经把框架搭好,可以根据需求进行下一步完善。
下一步我完善主要是需要与前端进行沟通,设计网页的API,如一个开门操作。通过这样的请求,利用CGI,完成比较复杂的操作。
在这里插入图片描述
本文提供的更多是一种思路,因为加入了操作系统,不只适用于单片机,只有是嵌入式设备都可以用,因为操作系统,lwip协议栈都是相同的。这个项目到此为止,希望有帮助的话多多点赞,收藏和评论。

版权声明:本文为CSDN博主「爱小羊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_30623327/article/details/122055933

生成海报
点赞 0

爱小羊

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

STM32串口发送接收数据

1.串口通信 我用的32是stm32f10x最小系统没有UART4和UART5 USART : 通用同步异步收发器 UART : 通用异步收发器 nRTS : 请求发送 nCTS : 请求接收 区别:USART指单片机的一个IO端

STM32的串口硬件流控(RS232/RS485)

流控的概念源于 RS232 这个标准,在 RS232 标准里面包含了串口、流控的定义。RS232 中的“RS”是Recommend Standard 的缩写,即”推荐标准“之意,它并不像 IEEE-12

CH579(M0内核)中断向量表偏移处理

通常情况下,为了产品后续的升级,程序都会分为两部分BootloaderApp,因为有两个程序,所以需要对中断向量表进行处理,否则,当程序已经跳转到app中运行,当中断产生的时候,响应函数仍然是Bootloader的中断函数,而不是app的 在S