diff --git a/pico_w/wifi/access_point/CMakeLists.txt b/pico_w/wifi/access_point/CMakeLists.txt index 6bc0a2eb4..3de1e95b3 100644 --- a/pico_w/wifi/access_point/CMakeLists.txt +++ b/pico_w/wifi/access_point/CMakeLists.txt @@ -9,11 +9,15 @@ target_include_directories(picow_access_point_background PRIVATE ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts ${CMAKE_CURRENT_LIST_DIR}/dhcpserver ${CMAKE_CURRENT_LIST_DIR}/dnsserver + ${PICO_LWIP_CONTRIB_PATH}/apps/httpd ) target_link_libraries(picow_access_point_background pico_cyw43_arch_lwip_threadsafe_background pico_stdlib + pico_lwip_http + ap_content + pico_status_led ) # You can change the address below to change the address of the access point pico_configure_ip4_address(picow_access_point_background PRIVATE @@ -31,13 +35,22 @@ target_include_directories(picow_access_point_poll PRIVATE ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts ${CMAKE_CURRENT_LIST_DIR}/dhcpserver ${CMAKE_CURRENT_LIST_DIR}/dnsserver + ${PICO_LWIP_CONTRIB_PATH}/apps/httpd ) target_link_libraries(picow_access_point_poll pico_cyw43_arch_lwip_poll pico_stdlib + pico_lwip_http + ap_content + pico_status_led ) # You can change the address below to change the address of the access point pico_configure_ip4_address(picow_access_point_poll PRIVATE CYW43_DEFAULT_IP_AP_ADDRESS 192.168.4.1 ) pico_add_extra_outputs(picow_access_point_poll) + +pico_add_library(ap_content NOFLAG) +pico_set_lwip_httpd_content(ap_content INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/content/index.shtml + ) diff --git a/pico_w/wifi/access_point/content/index.shtml b/pico_w/wifi/access_point/content/index.shtml new file mode 100644 index 000000000..1d26004b7 --- /dev/null +++ b/pico_w/wifi/access_point/content/index.shtml @@ -0,0 +1,13 @@ + +
+Led is
+ + + + \ No newline at end of file diff --git a/pico_w/wifi/access_point/lwipopts.h b/pico_w/wifi/access_point/lwipopts.h index 8571ed509..09dc83ddd 100644 --- a/pico_w/wifi/access_point/lwipopts.h +++ b/pico_w/wifi/access_point/lwipopts.h @@ -7,4 +7,14 @@ // This example uses a common include to avoid repetition #include "lwipopts_examples_common.h" +// Enable some httpd features +#define LWIP_HTTPD_CGI 1 +#define LWIP_HTTPD_SSI 1 +#define LWIP_HTTPD_SSI_MULTIPART 1 +#define LWIP_HTTPD_SUPPORT_POST 0 +#define LWIP_HTTPD_SSI_INCLUDE_TAG 0 + +// Generated file containing html data +#define HTTPD_FSDATA_FILE "pico_fsdata.inc" + #endif diff --git a/pico_w/wifi/access_point/picow_access_point.c b/pico_w/wifi/access_point/picow_access_point.c index c902fe877..ed9253fed 100644 --- a/pico_w/wifi/access_point/picow_access_point.c +++ b/pico_w/wifi/access_point/picow_access_point.c @@ -10,292 +10,92 @@ #include "pico/stdlib.h" #include "lwip/pbuf.h" -#include "lwip/tcp.h" +#include "lwip/ip4_addr.h" +#include "lwip/init.h" +#include "lwip/apps/httpd.h" // use lwip's httpd server to simplify example + +#include "pico/status_led.h" // libary for simplifying onboard LED usage on Pico W #include "dhcpserver.h" #include "dnsserver.h" -#define TCP_PORT 80 #define DEBUG_printf printf -#define POLL_TIME_S 5 -#define HTTP_GET "GET" -#define HTTP_RESPONSE_HEADERS "HTTP/1.1 %d OK\nContent-Length: %d\nContent-Type: text/html; charset=utf-8\nConnection: close\n\n" -#define LED_TEST_BODY "Led is %s
Turn led %s" -#define LED_PARAM "led=%d" -#define LED_TEST "/ledtest" -#define LED_GPIO 0 -#define HTTP_RESPONSE_REDIRECT "HTTP/1.1 302 Redirect\nLocation: http://%s" LED_TEST "\n\n" -typedef struct TCP_SERVER_T_ { - struct tcp_pcb *server_pcb; - bool complete; - ip_addr_t gw; -} TCP_SERVER_T; +static absolute_time_t wifi_connected_time; +bool led_state; +bool complete; -typedef struct TCP_CONNECT_STATE_T_ { - struct tcp_pcb *pcb; - int sent_len; - char headers[128]; - char result[256]; - int header_len; - int result_len; - ip_addr_t *gw; -} TCP_CONNECT_STATE_T; +static const char *switch_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { + printf("Switch cgi handler called\n"); -static err_t tcp_close_client_connection(TCP_CONNECT_STATE_T *con_state, struct tcp_pcb *client_pcb, err_t close_err) { - if (client_pcb) { - assert(con_state && con_state->pcb == client_pcb); - tcp_arg(client_pcb, NULL); - tcp_poll(client_pcb, NULL, 0); - tcp_sent(client_pcb, NULL); - tcp_recv(client_pcb, NULL); - tcp_err(client_pcb, NULL); - err_t err = tcp_close(client_pcb); - if (err != ERR_OK) { - DEBUG_printf("close failed %d, calling abort\n", err); - tcp_abort(client_pcb); - close_err = ERR_ABRT; - } - if (con_state) { - free(con_state); - } - } - return close_err; -} + // Get led state + led_state = status_led_get_state(); + printf("LED state: %d\n", led_state); -static void tcp_server_close(TCP_SERVER_T *state) { - if (state->server_pcb) { - tcp_arg(state->server_pcb, NULL); - tcp_close(state->server_pcb); - state->server_pcb = NULL; - } -} + // Now flip the state + status_led_set_state(!led_state); -static err_t tcp_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len) { - TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg; - DEBUG_printf("tcp_server_sent %u\n", len); - con_state->sent_len += len; - if (con_state->sent_len >= con_state->header_len + con_state->result_len) { - DEBUG_printf("all done\n"); - return tcp_close_client_connection(con_state, pcb, ERR_OK); - } - return ERR_OK; -} - -static int test_server_content(const char *request, const char *params, char *result, size_t max_result_len) { - int len = 0; - if (strncmp(request, LED_TEST, sizeof(LED_TEST) - 1) == 0) { - // Get the state of the led - bool value; - cyw43_gpio_get(&cyw43_state, LED_GPIO, &value); - int led_state = value; + // Update led state + led_state = status_led_get_state(); - // See if the user changed it - if (params) { - int led_param = sscanf(params, LED_PARAM, &led_state); - if (led_param == 1) { - if (led_state) { - // Turn led on - cyw43_gpio_set(&cyw43_state, LED_GPIO, true); - } else { - // Turn led off - cyw43_gpio_set(&cyw43_state, LED_GPIO, false); - } - } - } - // Generate result - if (led_state) { - len = snprintf(result, max_result_len, LED_TEST_BODY, "ON", 0, "OFF"); - } else { - len = snprintf(result, max_result_len, LED_TEST_BODY, "OFF", 1, "ON"); - } - } - return len; + return "/index.shtml"; } -err_t tcp_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { - TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg; - if (!p) { - DEBUG_printf("connection closed\n"); - return tcp_close_client_connection(con_state, pcb, ERR_OK); - } - assert(con_state && con_state->pcb == pcb); - if (p->tot_len > 0) { - DEBUG_printf("tcp_server_recv %d err %d\n", p->tot_len, err); -#if 0 - for (struct pbuf *q = p; q != NULL; q = q->next) { - DEBUG_printf("in: %.*s\n", q->len, q->payload); - } -#endif - // Copy the request into the buffer - pbuf_copy_partial(p, con_state->headers, p->tot_len > sizeof(con_state->headers) - 1 ? sizeof(con_state->headers) - 1 : p->tot_len, 0); - - // Handle GET request - if (strncmp(HTTP_GET, con_state->headers, sizeof(HTTP_GET) - 1) == 0) { - char *request = con_state->headers + sizeof(HTTP_GET); // + space - char *params = strchr(request, '?'); - if (params) { - if (*params) { - char *space = strchr(request, ' '); - *params++ = 0; - if (space) { - *space = 0; - } - } else { - params = NULL; - } - } - - // Generate content - con_state->result_len = test_server_content(request, params, con_state->result, sizeof(con_state->result)); - DEBUG_printf("Request: %s?%s\n", request, params); - DEBUG_printf("Result: %d\n", con_state->result_len); +static tCGI cgi_handlers[] = { + { "/switch.cgi", switch_cgi_handler } +}; - // Check we had enough buffer space - if (con_state->result_len > sizeof(con_state->result) - 1) { - DEBUG_printf("Too much result data %d\n", con_state->result_len); - return tcp_close_client_connection(con_state, pcb, ERR_CLSD); - } +static const char *ssi_tags[] = { + "led", + "not_led" +}; - // Generate web page - if (con_state->result_len > 0) { - con_state->header_len = snprintf(con_state->headers, sizeof(con_state->headers), HTTP_RESPONSE_HEADERS, - 200, con_state->result_len); - if (con_state->header_len > sizeof(con_state->headers) - 1) { - DEBUG_printf("Too much header data %d\n", con_state->header_len); - return tcp_close_client_connection(con_state, pcb, ERR_CLSD); - } +// 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 +static u16_t ssi_handler(int iIndex, char *pcInsert, int iInsertLen +#if LWIP_HTTPD_SSI_MULTIPART + , uint16_t current_tag_part, uint16_t *next_tag_part +#endif +){ + int printed = 0; + switch (iIndex) { + case 0: { // "led" + if (led_state) { + printed = snprintf(pcInsert, iInsertLen, "ON"); } else { - // Send redirect - con_state->header_len = snprintf(con_state->headers, sizeof(con_state->headers), HTTP_RESPONSE_REDIRECT, - ipaddr_ntoa(con_state->gw)); - DEBUG_printf("Sending redirect %s", con_state->headers); - } - - // Send the headers to the client - con_state->sent_len = 0; - err_t err = tcp_write(pcb, con_state->headers, con_state->header_len, 0); - if (err != ERR_OK) { - DEBUG_printf("failed to write header data %d\n", err); - return tcp_close_client_connection(con_state, pcb, err); - } - - // Send the body to the client - if (con_state->result_len) { - err = tcp_write(pcb, con_state->result, con_state->result_len, 0); - if (err != ERR_OK) { - DEBUG_printf("failed to write result data %d\n", err); - return tcp_close_client_connection(con_state, pcb, err); - } + printed = snprintf(pcInsert, iInsertLen, "OFF"); } + break; } - tcp_recved(pcb, p->tot_len); - } - pbuf_free(p); - return ERR_OK; -} - -static err_t tcp_server_poll(void *arg, struct tcp_pcb *pcb) { - TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg; - DEBUG_printf("tcp_server_poll_fn\n"); - return tcp_close_client_connection(con_state, pcb, ERR_OK); // Just disconnect clent? -} - -static void tcp_server_err(void *arg, err_t err) { - TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg; - if (err != ERR_ABRT) { - DEBUG_printf("tcp_client_err_fn %d\n", err); - tcp_close_client_connection(con_state, con_state->pcb, err); - } -} - -static err_t tcp_server_accept(void *arg, struct tcp_pcb *client_pcb, err_t err) { - TCP_SERVER_T *state = (TCP_SERVER_T*)arg; - if (err != ERR_OK || client_pcb == NULL) { - DEBUG_printf("failure in accept\n"); - return ERR_VAL; - } - DEBUG_printf("client connected\n"); - - // Create the state for the connection - TCP_CONNECT_STATE_T *con_state = calloc(1, sizeof(TCP_CONNECT_STATE_T)); - if (!con_state) { - DEBUG_printf("failed to allocate connect state\n"); - return ERR_MEM; - } - con_state->pcb = client_pcb; // for checking - con_state->gw = &state->gw; - - // setup connection to client - tcp_arg(client_pcb, con_state); - tcp_sent(client_pcb, tcp_server_sent); - tcp_recv(client_pcb, tcp_server_recv); - tcp_poll(client_pcb, tcp_server_poll, POLL_TIME_S * 2); - tcp_err(client_pcb, tcp_server_err); - - return ERR_OK; -} - -static bool tcp_server_open(void *arg, const char *ap_name) { - TCP_SERVER_T *state = (TCP_SERVER_T*)arg; - DEBUG_printf("starting server on port %d\n", TCP_PORT); - - struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_ANY); - if (!pcb) { - DEBUG_printf("failed to create pcb\n"); - return false; - } - - err_t err = tcp_bind(pcb, IP_ANY_TYPE, TCP_PORT); - if (err) { - DEBUG_printf("failed to bind to port %d\n",TCP_PORT); - return false; - } - - state->server_pcb = tcp_listen_with_backlog(pcb, 1); - if (!state->server_pcb) { - DEBUG_printf("failed to listen\n"); - if (pcb) { - tcp_close(pcb); + case 1: { // "not_led" + if (led_state) { + printed = snprintf(pcInsert, iInsertLen, "OFF"); + } else { + printed = snprintf(pcInsert, iInsertLen, "ON"); + } + break; } - return false; } - - tcp_arg(state->server_pcb, state); - tcp_accept(state->server_pcb, tcp_server_accept); - - printf("Try connecting to '%s' (press 'd' to disable access point)\n", ap_name); - return true; + return (u16_t)printed; } void key_pressed_func(void *param) { - assert(param); - TCP_SERVER_T *state = (TCP_SERVER_T*)param; + bool *complete = (bool *)param; int key = getchar_timeout_us(0); // get any pending key press but don't wait if (key == 'd' || key == 'D') { - cyw43_arch_lwip_begin(); - cyw43_arch_disable_ap_mode(); - cyw43_arch_lwip_end(); - state->complete = true; + *complete = true; } } int main() { stdio_init_all(); - TCP_SERVER_T *state = calloc(1, sizeof(TCP_SERVER_T)); - if (!state) { - DEBUG_printf("failed to allocate state\n"); - return 1; - } - if (cyw43_arch_init()) { DEBUG_printf("failed to initialise\n"); return 1; } // Get notified if the user presses a key - stdio_set_chars_available_callback(key_pressed_func, state); + stdio_set_chars_available_callback(key_pressed_func, &complete); const char *ap_name = "picow_test"; #if 1 @@ -313,26 +113,32 @@ int main() { #endif ip4_addr_t mask; - IP(state->gw).addr = PP_HTONL(CYW43_DEFAULT_IP_AP_ADDRESS); + ip4_addr_t gw; + IP(gw).addr = PP_HTONL(CYW43_DEFAULT_IP_AP_ADDRESS); IP(mask).addr = PP_HTONL(CYW43_DEFAULT_IP_MASK); #undef IP // Start the dhcp server dhcp_server_t dhcp_server; - dhcp_server_init(&dhcp_server, &state->gw, &mask); + dhcp_server_init(&dhcp_server, &gw, &mask); // Start the dns server dns_server_t dns_server; - dns_server_init(&dns_server, &state->gw); + dns_server_init(&dns_server, &gw); - if (!tcp_server_open(state, ap_name)) { - DEBUG_printf("failed to open server\n"); - return 1; - } + // setup http server + wifi_connected_time = get_absolute_time(); + cyw43_arch_lwip_begin(); + httpd_init(); + http_set_cgi_handlers(cgi_handlers, LWIP_ARRAYSIZE(cgi_handlers)); + http_set_ssi_handler(ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags)); + cyw43_arch_lwip_end(); - state->complete = false; - while(!state->complete) { + printf("Try connecting to '%s' (press 'd' to disable access point)\n", ap_name); + + complete = false; + while(!complete) { // the following #ifdef is only here so this same example can be used in multiple modes; // you do not need it in your code #if PICO_CYW43_ARCH_POLL @@ -349,10 +155,12 @@ int main() { sleep_ms(1000); #endif } - tcp_server_close(state); + + // Shutdown + cyw43_arch_disable_ap_mode(); dns_server_deinit(&dns_server); dhcp_server_deinit(&dhcp_server); cyw43_arch_deinit(); - printf("Test complete\n"); + printf("Complete\n"); return 0; }