Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lwip httpd examples #458

Merged
merged 1 commit into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ App|Description
[picow_tls_verify](pico_w/wifi/tls_client) | Demonstrates how to make a HTTPS request using TLS with certificate verification.
[picow_wifi_scan](pico_w/wifi/wifi_scan) | Scans for WiFi networks and prints the results.
[picow_udp_beacon](pico_w/wifi/udp_beacon) | A simple UDP transmitter.
[picow_httpd](pico_w/wifi/httpd) | Runs a LWIP HTTP server test app

#### FreeRTOS examples

Expand All @@ -144,6 +145,8 @@ App|Description
[picow_freertos_ping_nosys](pico_w/wifi/freertos/ping) | Runs the lwip-contrib/apps/ping test app under FreeRTOS in NO_SYS=1 mode.
[picow_freertos_ping_sys](pico_w/wifi/freertos/ping) | Runs the lwip-contrib/apps/ping test app under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. The test app uses the lwIP _socket_ API in this case.
[picow_freertos_ntp_client_socket](pico_w/wifi/freertos/ntp_client_socket) | Connects to an NTP server using the LwIP Socket API with FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode.
[pico_freertos_httpd_nosys](pico_w/wifi/freertos/httpd) | Runs a LWIP HTTP server test app under FreeRTOS in NO_SYS=1 mode.
[pico_freertos_httpd_sys](pico_w/wifi/freertos/httpd) | Runs a LWIP HTTP server test app under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode.

### Pico W Bluetooth

Expand Down
1 change: 1 addition & 0 deletions pico_w/wifi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ else()
add_subdirectory(tcp_server)
add_subdirectory(freertos)
add_subdirectory(udp_beacon)
add_subdirectory(httpd)

if (NOT PICO_MBEDTLS_PATH)
message("Skipping tls examples as PICO_MBEDTLS_PATH is not defined")
Expand Down
1 change: 1 addition & 0 deletions pico_w/wifi/freertos/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ else()
add_subdirectory(iperf)
add_subdirectory(ping)
add_subdirectory(ntp_client_socket)
add_subdirectory(httpd)
endif()
56 changes: 56 additions & 0 deletions pico_w/wifi/freertos/httpd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_LIST_DIR})

add_executable(pico_freertos_httpd_nosys
pico_freertos_httpd.c
)
target_compile_definitions(pico_freertos_httpd_nosys PRIVATE
WIFI_SSID=\"${WIFI_SSID}\"
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
)
target_include_directories(pico_freertos_httpd_nosys PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common FreeRTOSConfig
${CMAKE_CURRENT_LIST_DIR}/../.. # for our common lwipopts
${PICO_LWIP_CONTRIB_PATH}/apps/httpd
)
target_link_libraries(pico_freertos_httpd_nosys
pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_http
pico_lwip_mdns
pico_stdlib
FreeRTOS-Kernel-Heap4 # FreeRTOS kernel and dynamic heap
pico_freertos_httpd_content
)
pico_add_extra_outputs(pico_freertos_httpd_nosys)

add_executable(pico_freertos_httpd_sys
pico_freertos_httpd.c
)
target_compile_definitions(pico_freertos_httpd_sys PRIVATE
WIFI_SSID=\"${WIFI_SSID}\"
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
NO_SYS=0 # don't want NO_SYS (generally this would be in your lwipopts.h)
LWIP_SOCKET=1 # we need the socket API (generally this would be in your lwipopts.h)
)
target_include_directories(pico_freertos_httpd_sys PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common FreeRTOSConfig
${CMAKE_CURRENT_LIST_DIR}/../.. # for our common lwipopts
${PICO_LWIP_CONTRIB_PATH}/apps/httpd
)
target_link_libraries(pico_freertos_httpd_sys
pico_cyw43_arch_lwip_sys_freertos
pico_lwip_http
pico_lwip_mdns
pico_stdlib
FreeRTOS-Kernel-Heap4 # FreeRTOS kernel and dynamic heap
pico_freertos_httpd_content
)
pico_add_extra_outputs(pico_freertos_httpd_sys)

pico_add_library(pico_freertos_httpd_content NOFLAG)
pico_set_lwip_httpd_content(pico_freertos_httpd_content INTERFACE
${CMAKE_CURRENT_LIST_DIR}/content/404.html
${CMAKE_CURRENT_LIST_DIR}/content/index.shtml
${CMAKE_CURRENT_LIST_DIR}/content/test.shtml
)
7 changes: 7 additions & 0 deletions pico_w/wifi/freertos/httpd/FreeRTOSConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

// This example uses a common include to avoid repetition
#include "FreeRTOSConfig_examples_common.h"

#endif
10 changes: 10 additions & 0 deletions pico_w/wifi/freertos/httpd/content/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<html>
<head>
<title>Pico httpd example</title>
</head>
<body>
<h1>Pico httpd example</h1>
<h2>404 - Page not found</h2>
<p>Sorry, the page you are requesting was not found on this server. </p>
</body>
</html>
12 changes: 12 additions & 0 deletions pico_w/wifi/freertos/httpd/content/index.shtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>
<head>
<title>Pico httpd example</title>
</head>
<body>
<h1>Pico httpd example</h1>
<p><!--#welcome--></p>
<p>Uptime is <!--#uptime--> seconds</p>
<p><a href="/?test">CGI test</a></p>
<p>Led is <!--#ledstate--> click <a href="/?toggleled">here</a> to toggle the led
</body>
</html>
11 changes: 11 additions & 0 deletions pico_w/wifi/freertos/httpd/content/test.shtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html>
<head>
<title>Pico cgi test</title>
</head>
<body>
<h1>Pico cgi test</h1>
<p><table><!--#table--></table></p>
<p>Test result: <!--#status--></p>
<p><a href="/">Go back</a></p>
</body>
</html>
36 changes: 36 additions & 0 deletions pico_w/wifi/freertos/httpd/lwipopts.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef _LWIPOPTS_H
#define _LWIPOPTS_H

// Generally you would define your own explicit list of lwIP options
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html)
//
// This example uses a common include to avoid repetition
#include "lwipopts_examples_common.h"

// The following is needed to test mDns
#define LWIP_MDNS_RESPONDER 1
#define LWIP_IGMP 1
#define LWIP_NUM_NETIF_CLIENT_DATA 1
#define MDNS_RESP_USENETIF_EXTCALLBACK 1
#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + 3)
#define MEMP_NUM_TCP_PCB 12

// Enable cgi and ssi
#define LWIP_HTTPD_CGI 1
#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_SSI_MULTIPART 1

#if !NO_SYS
#define TCPIP_THREAD_STACKSIZE 2048 // mDNS needs more stack
#define DEFAULT_THREAD_STACKSIZE 1024
#define DEFAULT_RAW_RECVMBOX_SIZE 8
#define TCPIP_MBOX_SIZE 8
#define LWIP_TIMEVAL_PRIVATE 0

// not necessary, can be done either way
#define LWIP_TCPIP_CORE_LOCKING_INPUT 1
#endif

#define HTTPD_FSDATA_FILE "pico_fsdata.inc"

#endif
217 changes: 217 additions & 0 deletions pico_w/wifi/freertos/httpd/pico_freertos_httpd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/**
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "pico/cyw43_arch.h"
#include "pico/stdlib.h"

#include "lwip/ip4_addr.h"
#include "lwip/apps/mdns.h"
#include "lwip/init.h"
#include "lwip/apps/httpd.h"

#include "FreeRTOS.h"
#include "task.h"

void httpd_init(void);

static absolute_time_t wifi_connected_time;
static bool led_on = false;

#ifndef RUN_FREERTOS_ON_CORE
#define RUN_FREERTOS_ON_CORE 0
#endif

#define TEST_TASK_PRIORITY ( tskIDLE_PRIORITY + 1UL )

#if LWIP_MDNS_RESPONDER
static void srv_txt(struct mdns_service *service, void *txt_userdata)
{
err_t res;
LWIP_UNUSED_ARG(txt_userdata);

res = mdns_resp_add_service_txtitem(service, "path=/", 6);
LWIP_ERROR("mdns add service txt failed\n", (res == ERR_OK), return);
}
#endif

// Return some characters from the ascii representation of the mac address
// e.g. 112233445566
// chr_off is index of character in mac to start
// chr_len is length of result
// chr_off=8 and chr_len=4 would return "5566"
// Return number of characters put into destination
static size_t get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest_in) {
static const char hexchr[16] = "0123456789ABCDEF";
uint8_t mac[6];
char *dest = dest_in;
assert(chr_off + chr_len <= (2 * sizeof(mac)));
cyw43_hal_get_mac(idx, mac);
for (; chr_len && (chr_off >> 1) < sizeof(mac); ++chr_off, --chr_len) {
*dest++ = hexchr[mac[chr_off >> 1] >> (4 * (1 - (chr_off & 1))) & 0xf];
}
return dest - dest_in;
}

static const char *cgi_handler_test(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
if (!strcmp(pcParam[0], "test")) {
return "/test.shtml";
} else if (strcmp(pcParam[0], "toggleled") == 0) {
led_on = !led_on;
cyw43_gpio_set(&cyw43_state, 0, led_on);
}
return "/index.shtml";
}

static tCGI cgi_handlers[] = {
{ "/", cgi_handler_test },
{ "/index.shtml", cgi_handler_test },
};

// Note that the buffer size is limited by LWIP_HTTPD_MAX_TAG_INSERT_LEN, so use LWIP_HTTPD_SSI_MULTIPART to return larger amounts of data
u16_t ssi_example_ssi_handler(int iIndex, char *pcInsert, int iInsertLen
#if LWIP_HTTPD_SSI_MULTIPART
, uint16_t current_tag_part, uint16_t *next_tag_part
#endif
) {
size_t printed;
switch (iIndex) {
case 0: { /* "status" */
printed = snprintf(pcInsert, iInsertLen, "Pass");
break;
}
case 1: { /* "welcome" */
printed = snprintf(pcInsert, iInsertLen, "Hello from Pico");
break;
}
case 2: { /* "uptime" */
uint64_t uptime_s = absolute_time_diff_us(wifi_connected_time, get_absolute_time()) / 1e6;
printed = snprintf(pcInsert, iInsertLen, "%"PRIu64, uptime_s);
break;
}
case 3: { // "ledstate"
printed = snprintf(pcInsert, iInsertLen, "%s", led_on ? "ON" : "OFF");
break;
}
#if LWIP_HTTPD_SSI_MULTIPART
case 4: { /* "table" */
printed = snprintf(pcInsert, iInsertLen, "<tr><td>This is table row number %d</td></tr>", current_tag_part + 1);
// Leave "next_tag_part" unchanged to indicate that all data has been returned for this tag
if (current_tag_part < 9) {
*next_tag_part = current_tag_part + 1;
}
break;
}
#endif
default: { /* unknown tag */
printed = 0;
break;
}
}
return (u16_t)printed;
}

// Be aware of LWIP_HTTPD_MAX_TAG_NAME_LEN
static const char * ssi_tags[] = {
"status",
"welcome",
"uptime",
"ledstate",
"table",
};

void main_task(__unused void *params) {
if (cyw43_arch_init()) {
printf("failed to initialise\n");
return;
}

cyw43_arch_enable_sta_mode();

char hostname[sizeof(CYW43_HOST_NAME) + 4];
memcpy(&hostname[0], CYW43_HOST_NAME, sizeof(CYW43_HOST_NAME) - 1);
get_mac_ascii(CYW43_HAL_MAC_WLAN0, 8, 4, &hostname[sizeof(CYW43_HOST_NAME) - 1]);
hostname[sizeof(hostname) - 1] = '\0';
netif_set_hostname(&cyw43_state.netif[CYW43_ITF_STA], hostname);

printf("Connecting to WiFi...\n");
if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) {
printf("failed to connect.\n");
exit(1);
} else {
printf("Connected.\n");
}

wifi_connected_time = get_absolute_time();

#if LWIP_MDNS_RESPONDER
mdns_resp_init();
printf("mdns host name %s.local\n", hostname);
#if LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 2
mdns_resp_add_netif(&cyw43_state.netif[CYW43_ITF_STA], hostname);
mdns_resp_add_service(&cyw43_state.netif[CYW43_ITF_STA], "picow_freertos_httpd", "_http", DNSSD_PROTO_TCP, 80, srv_txt, NULL);
#else
mdns_resp_add_netif(&cyw43_state.netif[CYW43_ITF_STA], hostname, 60);
mdns_resp_add_service(&cyw43_state.netif[CYW43_ITF_STA], "picow_freertos_httpd", "_http", DNSSD_PROTO_TCP, 80, 60, srv_txt, NULL);
#endif
#endif

printf("\nReady, running httpd at %s\n", ip4addr_ntoa(netif_ip4_addr(netif_list)));
httpd_init();

http_set_cgi_handlers(cgi_handlers, LWIP_ARRAYSIZE(cgi_handlers));
http_set_ssi_handler(ssi_example_ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags));

while(true) {
vTaskDelay(100);
}

#if LWIP_MDNS_RESPONDER
mdns_resp_remove_netif(&cyw43_state.netif[CYW43_ITF_STA]);
#endif

cyw43_arch_deinit();
}

void vLaunch( void) {
TaskHandle_t task;
xTaskCreate(main_task, "TestMainThread", configMINIMAL_STACK_SIZE, NULL, TEST_TASK_PRIORITY, &task);

#if NO_SYS && configUSE_CORE_AFFINITY && configNUM_CORES > 1
// we must bind the main task to one core (well at least while the init is called)
// (note we only do this in NO_SYS mode, because cyw43_arch_freertos
// takes care of it otherwise)
vTaskCoreAffinitySet(task, 1);
#endif

/* Start the tasks and timer running. */
vTaskStartScheduler();
}

int main( void )
{
stdio_init_all();

/* Configure the hardware ready to run the demo. */
const char *rtos_name;
#if ( portSUPPORT_SMP == 1 )
rtos_name = "FreeRTOS SMP";
#else
rtos_name = "FreeRTOS";
#endif

#if ( portSUPPORT_SMP == 1 ) && ( configNUM_CORES == 2 )
printf("Starting %s on both cores:\n", rtos_name);
vLaunch();
#elif ( RUN_FREE_RTOS_ON_CORE == 1 )
printf("Starting %s on core 1:\n", rtos_name);
multicore_launch_core1(vLaunch);
while (true);
#else
printf("Starting %s on core 0:\n", rtos_name);
vLaunch();
#endif
return 0;
}
Loading
Loading