本文章介紹使用ws2812製作一個大型的7段顯示器的數位時鐘。
使用Raspberry Pi Pico W微處理器開發板。
使用Lwip httpd, mdns與sntp application。
- lwipopts.h依照使用的lwip application需要增加的設定值:
- Lwip httpd:
網頁檔案儲存在Flash中,因此需要將檔案編譯成程式碼的一部分,使用makefsdata perl程式將網頁檔編譯成_fsdata.c。makefsdata程式碼參閱路徑:pico-sdk/lib/lwip/src/apps/http/makefsdata。 - Lwip mdns:
將網站網址定義成picow_led_clock.local,方便存取網站。MEMP_NUM_SYS_TIMEOUT需要依據使用services的數量增加。 - Lwip sntp:
使用Lwip sntp取得NTP時間,定期更新微處理器系統時間。自訂的更新系統時間函式定義在
成果展示:
程式碼:
CMakeLists.txt:
# perl makefsdata
find_package(Perl)
if(NOT PERL_FOUND)
message(FATAL_ERROR "Perl is needed for generating the fsdata.c file")
endif()
set(MAKE_FS_DATA_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/mkfsdata/makefsdata)
if (EXISTS ${MAKE_FS_DATA_SCRIPT})
message("Find makefsdata script")
message("Running makefsdata script")
execute_process(COMMAND
perl ${MAKE_FS_DATA_SCRIPT}
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
ECHO_OUTPUT_VARIABLE
ECHO_ERROR_VARIABLE
)
file(RENAME fsdata.c _fsdata.c)
endif()
# Generated Cmake Pico project file
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/duser/pico/pico-sdk")
set(PICO_BOARD pico_w CACHE STRING "Board type")
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
project(picow_ws2812_clock C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
add_definitions(
-DSNTP_SET_SYSTEM_TIME=sntp_set_system_time
)
# Add executable. Default name is the project name, version 0.1
add_executable(picow_ws2812_clock
picow_ws2812_clock.c
wifi_scan/wifi_scan.c
ap_http_server/ap_http_server.c
cJSON/cJSON.c
dhcpserver/dhcpserver.c)
pico_set_program_name(picow_ws2812_clock "picow_ws2812_clock")
pico_set_program_version(picow_ws2812_clock "0.1")
pico_enable_stdio_uart(picow_ws2812_clock 1)
pico_enable_stdio_usb(picow_ws2812_clock 0)
# Add the standard library to the build
target_link_libraries(picow_ws2812_clock
pico_stdlib)
# Add the standard include files to the build
target_include_directories(picow_ws2812_clock PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
${CMAKE_CURRENT_LIST_DIR}/dhcpserver
${CMAKE_CURRENT_LIST_DIR}/cJSON
${CMAKE_CURRENT_LIST_DIR}/ap_http_server
${CMAKE_CURRENT_LIST_DIR}/wifi_scan
)
# Add any user requested libraries
target_link_libraries(picow_ws2812_clock
hardware_pio
hardware_timer
hardware_clocks
pico_cyw43_arch_lwip_poll
pico_lwip_http
pico_lwip_mdns
pico_lwip_sntp
hardware_flash
hardware_watchdog
hardware_rtc
)
add_subdirectory(ws2812)
target_link_libraries(picow_ws2812_clock
ws2812
)
pico_add_extra_outputs(picow_ws2812_clock)
lwipopts.h:
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
// allow override in some examples
#ifndef NO_SYS
#define NO_SYS 1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC 1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC 0
#endif
#define MEM_ALIGNMENT 4
#define MEM_SIZE 4000
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 24
#define LWIP_ARP 1
#define LWIP_ETHERNET 1
#define LWIP_ICMP 1
#define LWIP_RAW 1
#define TCP_WND (8 * TCP_MSS)
#define TCP_MSS 1460
#define TCP_SND_BUF (8 * TCP_MSS)
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_NETCONN 0
#define MEM_STATS 0
#define SYS_STATS 0
#define MEMP_STATS 0
#define LINK_STATS 0
// #define ETH_PAD_SIZE 2
#define LWIP_CHKSUM_ALGORITHM 3
#define LWIP_DHCP 1
#define LWIP_IPV4 1
#define LWIP_TCP 1
#define LWIP_UDP 1
#define LWIP_DNS 1
#define LWIP_TCP_KEEPALIVE 1
#define LWIP_NETIF_TX_SINGLE_PBUF 1
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0
#ifndef NDEBUG
#define LWIP_DEBUG 1
#define LWIP_STATS 1
#define LWIP_STATS_DISPLAY 1
#endif
#define ETHARP_DEBUG LWIP_DBG_OFF
#define NETIF_DEBUG LWIP_DBG_OFF
#define PBUF_DEBUG LWIP_DBG_OFF
#define API_LIB_DEBUG LWIP_DBG_OFF
#define API_MSG_DEBUG LWIP_DBG_OFF
#define SOCKETS_DEBUG LWIP_DBG_OFF
#define ICMP_DEBUG LWIP_DBG_OFF
#define INET_DEBUG LWIP_DBG_OFF
#define IP_DEBUG LWIP_DBG_OFF
#define IP_REASS_DEBUG LWIP_DBG_OFF
#define RAW_DEBUG LWIP_DBG_OFF
#define MEM_DEBUG LWIP_DBG_OFF
#define MEMP_DEBUG LWIP_DBG_OFF
#define SYS_DEBUG LWIP_DBG_OFF
#define TCP_DEBUG LWIP_DBG_OFF
#define TCP_INPUT_DEBUG LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
#define TCP_RTO_DEBUG LWIP_DBG_OFF
#define TCP_CWND_DEBUG LWIP_DBG_OFF
#define TCP_WND_DEBUG LWIP_DBG_OFF
#define TCP_FR_DEBUG LWIP_DBG_OFF
#define TCP_QLEN_DEBUG LWIP_DBG_OFF
#define TCP_RST_DEBUG LWIP_DBG_OFF
#define UDP_DEBUG LWIP_DBG_OFF
#define TCPIP_DEBUG LWIP_DBG_OFF
#define PPP_DEBUG LWIP_DBG_OFF
#define SLIP_DEBUG LWIP_DBG_OFF
#define DHCP_DEBUG LWIP_DBG_OFF
// httpd
#define LWIP_HTTPD 1
#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_CGI 1
#define LWIP_HTTPD_SSI_MULTIPART 1
#define LWIP_HTTPD_SUPPORT_POST 1
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
#define HTTPD_FSDATA_FILE "_fsdata.c"
// mDNS
#define LWIP_MDNS_RESPONDER 1
#define LWIP_IGMP 1
#define LWIP_AUTOIP 1
#define MEMP_NUM_UDP_PCB (4+1)
#define LWIP_NUM_NETIF_CLIENT_DATA 1
#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + 4)
// SNTP
#define SNTP_SUPPORT 1
#define SNTP_SERVER_DNS 1
//#define SNTP_UPDATE_DELAY 86400
#define SNTP_STARTUP_DELAY 0
#endif /* __LWIPOPTS_H__ */
picow_ws2812_clock.c:
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "pico/cyw43_arch.h"
#include "ws2812.h"
#include "hardware/rtc.h"
// Lwip application: httpd, mdns and sntp
#include "lwip/apps/httpd.h"
#include "lwip/apps/mdns.h"
#include "lwip/apps/sntp.h"
#include "ap_http_server.h"
#include "cJSON.h"
#include "picow_ws2812_clock.h"
// 7-segment digit
uint8_t digit[10][7] = {
{1,1,1,1,1,1,0}, //0
{1,0,0,0,0,1,0}, //1
{0,1,1,0,1,1,1}, //2
{1,1,0,0,1,1,1}, //3
{1,0,0,1,0,1,1}, //4
{1,1,0,1,1,0,1}, //5
{1,1,1,1,1,0,1}, //6
{1,0,0,0,1,1,0}, //7
{1,1,1,1,1,1,1}, //8
{1,0,0,1,1,1,1}, //9
};
PIO pio = pio0;
uint8_t sm_digit=0;
uint8_t sm_colon=1;
uint32_t status_colon[4] = {0x00ff00, 0x0000ff, 0xffffff,POWER_SAVE_COLOR};
struct clock_config_t clock_config;
uint8_t pre_min=0;
bool connect_to_wifi_ssid() {
if (!clock_config.flash_data) return false;
cyw43_arch_enable_sta_mode();
printf("\n\n==========================\n"
"Connecting to WiFi:%s\n"
"==============================\n", clock_config.ssid);
if (cyw43_arch_wifi_connect_timeout_ms(clock_config.ssid, clock_config.pass, CYW43_AUTH_WPA2_AES_PSK, 10000)) {
printf("wifi sta connect error ssid:%s\n", clock_config.ssid);
//cyw43_arch_deinit();
return false;
}
ip_addr_t addr = cyw43_state.netif->ip_addr;
printf("connect successfully. get IP: %s\n", ipaddr_ntoa(&addr));
return true;
}
void display_digit(uint8_t number, uint32_t color) {
for (int i=0; i < 7; i++) {
if (digit[number][i] == 1) {
for (int k=0; k < 3;k++)
ws2812_put_grb_pixel(pio, sm_digit,color);
}
else {
for (int k=0; k < 3;k++)
ws2812_put_grb_pixel(pio, sm_digit,0x000000);
}
}
}
void set_led_clock_color() {
uint32_t color;
sscanf(clock_config.led_color, "#%x", &color);
clock_config.display_color = (color&0xff0000) >> 8 | (color&0x00ff00) << 8 | color&0x0000ff;
printf("color:%x, %s\n", color,clock_config.led_color);
}
void display_clock_digits(bool force) {
datetime_t date_time;
uint8_t h1, h0, min1, min0, h,min;
if (rtc_get_datetime(&date_time)) {
if (date_time.sec == 0 || pre_min != date_time.min || force) {
pre_min = date_time.min;
} else {
return ;
}
min = date_time.min;
h = date_time.hour;
min0 = min%10;
min1 = min/10;
h0 = h %10;
h1 = h/10;
display_digit(min0, clock_config.display_color);
display_digit(min1, clock_config.display_color);
display_digit(h0, clock_config.display_color);
display_digit(h1, clock_config.display_color);
}
}
bool timer_callback(repeating_timer_t* rt) {
display_clock_digits(false);
return true;
}
bool colon_timer_callback(repeating_timer_t* rt) {
static uint8_t s=0;
if (!s) {
for (int i=0; i <2; i++)
ws2812_put_grb_pixel(pio, sm_colon, status_colon[clock_config.colon_state]);
}
else {
for (int i=0; i <2; i++)
ws2812_put_grb_pixel(pio, sm_colon, 0x000000);
}
s =(s+1)%2;
return true;
}
#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);
}
static void mdns_example_report(struct netif* netif, u8_t result, s8_t service)
{
LWIP_PLATFORM_DIAG(("mdns status[netif %d][service %d]: %d\n", netif->num, service, result));
}
#endif
//SNTP
void sntp_set_system_time(uint32_t sec, uint32_t us)
{
char buf[32];
struct tm current_time_val;
time_t current_time = (sec+atoi(clock_config.timezone)*60*60);
struct tm* p= gmtime(¤t_time);
datetime_t rtc_time;
rtc_time.year = p->tm_year+1900;
rtc_time.month = p->tm_mon+1;
rtc_time.day = p->tm_mday;
rtc_time.hour = p->tm_hour;
rtc_time.min = p->tm_min;
rtc_time.sec = p->tm_sec;
rtc_time.dotw = p->tm_wday;
rtc_set_datetime(&rtc_time);
}
int main()
{
stdio_init_all();
if (cyw43_arch_init()) {
printf("failed to initialise\n");
return 1;
}
// pio state machine 0 for ws2812 digit, 1 for colom
ws2812_init(pio, sm_digit, 16); //GPIO pin 16
ws2812_init(pio, sm_colon, 17); //GPIO pin 17
// rtc initialize value
rtc_init();
datetime_t rtc_time;
rtc_time.year=2024;
rtc_time.month= 1;
rtc_time.day = 1;
rtc_time.hour = 1;
rtc_time.min = 1;
rtc_time.sec = 1;
rtc_time.dotw=1;
rtc_set_datetime(&rtc_time);
clock_config.colon_state=SNTP_ERROR;
// initialize clock configuration.
if (!read_config_from_flash()) {
strcpy(clock_config.ssid,""); //default values if can not read from flash.
strcpy(clock_config.pass,"");;
strcpy(clock_config.led_color, "#1f1f1f");
strcpy(clock_config.ntp_server, "pool.ntp.org");
strcpy(clock_config.ntp_interval,"3");
strcpy(clock_config.timezone,"+0");
clock_config.flash_data=0;
} else {
clock_config.flash_data=1;
}
set_led_clock_color();
repeating_timer_t rt, rt1;
add_repeating_timer_ms(-1000, timer_callback, NULL, &rt); // for 7-segment timer
add_repeating_timer_ms(-500, colon_timer_callback, NULL, &rt1);
if (!connect_to_wifi_ssid()) {
clock_config.wifi_connected=0; // change to AP mode if not connect to WIFI lan
ap_http_server_start();
clock_config.colon_state=WIFI_NOT_CONNECT;
} else {
clock_config.wifi_connected=1;
http_server_start();
clock_config.colon_state=WIFI_OK;
}
// mDNS: picow_led_clock.local
#if LWIP_MDNS_RESPONDER
mdns_resp_register_name_result_cb(mdns_example_report);
mdns_resp_init();
if (mdns_resp_add_netif(netif_default, "picow_led_clock")== ERR_OK) {
printf("mDNS add successfully\n");
} else {
printf("mDNS failure\n");
}
mdns_resp_add_service(netif_default, "myweb", "_http", DNSSD_PROTO_TCP, 80, srv_txt, NULL);
mdns_resp_announce(netif_default);
#endif
// enable SNTP
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, clock_config.ntp_server);
sntp_init();
if (!sntp_enabled()) {
clock_config.colon_state=SNTP_ERROR;
printf("sntp not enable\n");
}
clock_config.prev_colon_state = clock_config.colon_state;
while (1) {
cyw43_arch_poll();
sleep_ms(1);
}
return 0;
}
picow_ws2812_clock.h:
#ifndef __PICOW_WS2812_CLOCK_H_
#define __PROJ__PICOW_WS2812_CLOCK_H_ECT_H_
#define POWER_SAVE_COLOR (0x050505)
/* colon color indicator
red: wifi not connected to lan
blue: sntp not enabled
white : correct;
*/
enum {
WIFI_NOT_CONNECT=0,
SNTP_ERROR=1,
WIFI_OK=2,
POWER_SAVE=3,
};
struct clock_config_t {
char ssid[50];
char pass[50];
char led_color[10]; //0x000000
char ntp_server[100];
char ntp_interval[3]; //3~12
char timezone[4]; // -12~+14
uint8_t wifi_connected;
uint8_t flash_data; //init
uint32_t display_color;
uint8_t colon_state;
uint8_t prev_colon_state;
};
#endif
ap_http_server.c:
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "lwip/apps/httpd.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "hardware/watchdog.h"
#include "hardware/rtc.h"
#include "lwip/apps/sntp.h"
#include "ap_http_server.h"
#include "wifi_scan.h"
#include "dhcpserver.h"
#include "cJSON.h"
#include "picow_ws2812_clock.h"
enum {
LED_COLOR_PAGE=1,
NTP_SERVER_PAGE,
LOCAL_TIME_PAGE,
WIFI_CONN_PAGE,
} POST_PAGE;
//struct HTTP_POST_SERVER_T
typedef struct HTTP_POST_SERVER_T_ {
void *current_connection;
//void *valid_connection;
char ssid[WIFI_PASS_BUFSIZE];
char pass[WIFI_PASS_BUFSIZE];
bool post_recv;
uint8_t post_page;
} HTTP_POST_SERVER_T;
HTTP_POST_SERVER_T *server=NULL;
// struct HTTP_POST_SERVER_T
SCAN_APS_T *aps;
dhcp_server_t dhcp_server;
#define WIFI_AP_SSID "PicoW"
#define WIFI_AP_PASSWORD "ap_password"
struct clock_config_t clock_config;
char* urldecode(char* str) {
char tmpstr[WIFI_PASS_BUFSIZE];
int j=0;
int i=0;
char tmpval[5];
while(i < strlen(str)) {
if (str[i] == '%') {
sprintf(tmpval, "%s%c%c", "0x",str[i+1], str[i+2]);
tmpstr[j] = strtol(tmpval, NULL, 16);
i+=3;
} else {
tmpstr[j]=str[i];
i++;
}
j++;
}
tmpstr[j]='\0';
strcpy(str, tmpstr);
return str;
}
bool read_config_from_flash() {
char flash_buff[256*FLASH_STORAGE_PAGES];
memset(flash_buff,0,256*FLASH_STORAGE_PAGES);
snprintf(flash_buff, 256*FLASH_STORAGE_PAGES, "%s",(uint8_t*)(XIP_BASE+FLASH_OFFSET));
if (!flash_buff) return false;
printf("ff:%s\n", flash_buff);
cJSON *config_flash = cJSON_CreateObject();
config_flash = cJSON_Parse(flash_buff);
if (config_flash) {
strcpy(clock_config.ssid, cJSON_GetStringValue(cJSON_GetObjectItem(config_flash, "ssid")));
strcpy(clock_config.pass, cJSON_GetStringValue(cJSON_GetObjectItem(config_flash, "pass")));
strcpy(clock_config.led_color, cJSON_GetStringValue(cJSON_GetObjectItem(config_flash, "ledcolor")));
strcpy(clock_config.ntp_server, cJSON_GetStringValue(cJSON_GetObjectItem(config_flash, "ntpserver")));
strcpy(clock_config.ntp_interval, cJSON_GetStringValue(cJSON_GetObjectItem(config_flash, "ntpinterval")));
strcpy(clock_config.timezone, cJSON_GetStringValue(cJSON_GetObjectItem(config_flash, "timezone")));
} else {
return false;
}
cJSON_Delete(config_flash);
printf("read configuration from flash oK\n");
return true;
}
void save_to_flash(bool reboot) {
char flash_buff[256*FLASH_STORAGE_PAGES];
memset(flash_buff, 0, 256*FLASH_STORAGE_PAGES);
sprintf(flash_buff, "{\"ssid\":\"%s\",\"pass\":\"%s\", \"ledcolor\":\"%s\", \"ntpserver\":\"%s\", \"ntpinterval\":\"%s\", \"timezone\":\"%s\"}",
clock_config.ssid, clock_config.pass, clock_config.led_color, clock_config.ntp_server, clock_config.ntp_interval, clock_config.timezone);
uint32_t ints = save_and_disable_interrupts();
flash_range_erase(FLASH_OFFSET, 4096); // one sector
flash_range_program(FLASH_OFFSET, flash_buff, 256*FLASH_STORAGE_PAGES);
restore_interrupts(ints);
if (reboot)
watchdog_enable(5000, false); // after save data to flash, reboot in 5 seconds
}
// set_led_clock_color() and display_clock_digits() define at picow_ws2812_clock.c
extern void set_led_clock_color();
extern void display_clock_digits(bool force);
/* ==== cgi begin ======*/
const char *
cgi_handler_wifi_refresh(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
scan_aps(aps, 20000);
return "/select_wifi.shtml"; //return to index.shtml
}
const char *
cgi_handler_led_color(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
printf("led_cgi\n");
for (int i = 0; i < iNumParams; i++) {
if (strcmp(pcParam[i], "psave") == 0) {
if (strcmp(pcValue[i], "on") == 0) {
clock_config.display_color = POWER_SAVE_COLOR;
clock_config.prev_colon_state=clock_config.colon_state;
clock_config.colon_state=POWER_SAVE; //POWER_SAVE
} else {
set_led_clock_color();
clock_config.colon_state=clock_config.prev_colon_state;
}
display_clock_digits(true);
}
}
return "/index.shtml"; //return to index.shtml
}
const char *
cgi_handler_set_local_time(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
printf("local time\n");
return "/index.shtml"; //return to index.shtml
}
const char *
cgi_handler_ntp_server(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
printf("ntp_server\n");
return "/index.shtml"; //return to index.shtml
}
static const tCGI cgi_handlers[] = {
{
//* Html request for "/wifi_refresh.cgi" will start cgi_handler_wifi_refresh
"/wifi_refresh.cgi", cgi_handler_wifi_refresh
},
{
"/led_color.cgi", cgi_handler_led_color
},
{
"/set_local_time.cgi", cgi_handler_set_local_time
},
{
"/ntp_server.cgi", cgi_handler_ntp_server
},
};
/* ==== cgi end ======*/
/*===== post begin =====*/
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) {
LWIP_UNUSED_ARG(connection);
LWIP_UNUSED_ARG(http_request);
LWIP_UNUSED_ARG(http_request_len);
LWIP_UNUSED_ARG(content_len);
LWIP_UNUSED_ARG(post_auto_wnd);
server->post_recv=false;
server->post_page=0;
if (!memcmp(uri, "/wifi_conn.shtml", 17)) {
server->post_page = WIFI_CONN_PAGE;
}
if (!memcmp(uri, "/led_color.cgi", 15)) {
server->post_page = LED_COLOR_PAGE;
}
if (!memcmp(uri, "/ntp_server.cgi", 16)) {
server->post_page = NTP_SERVER_PAGE;
}
if (!memcmp(uri, "/set_local_time.cgi", 20)) {
server->post_page = LOCAL_TIME_PAGE;
}
if (server->post_page) {
if (server->current_connection != connection) {
server->current_connection = connection;
snprintf(response_uri, response_uri_len, "/index.shtml"); // default : return main page
*post_auto_wnd = 1;
return ERR_OK;
}
}
return ERR_VAL;
}
bool get_post_data_param(struct pbuf *p, char* param_name, char* param_value) {
u16_t token, value_offset, len;
char param_name_eq[100];
sprintf(param_name_eq, "%s=", param_name);
token = pbuf_memfind(p, param_name_eq, strlen(param_name_eq), 0);
if ((token != 0xFFFF)) {
u16_t value_offset = token + strlen(param_name_eq);
u16_t len = 0;
u16_t tmp;
/* find len */
tmp = pbuf_memfind(p, "&", 1, value_offset);
if (tmp != 0xFFFF) {
len = tmp - value_offset;
} else {
len = p->tot_len - value_offset;
}
if ((len > 0)) {
char* tmpstr= (char*)pbuf_get_contiguous(p, ¶m_name_eq, sizeof(param_name_eq), len, value_offset);
tmpstr[len]=0;
strcpy(param_value, urldecode(tmpstr));
} else {
return false;
}
} else {
return false;
}
return true;
}
err_t httpd_post_receive_data(void *connection, struct pbuf *p) {
err_t ret;
LWIP_ASSERT("NULL pbuf", p != NULL);
if (server->current_connection == connection) {
if (server->post_page == WIFI_CONN_PAGE){
if (get_post_data_param(p, "ssid", server->ssid) &&
get_post_data_param(p, "pass", server->pass)) {
strcpy(clock_config.ssid, server->ssid);
strcpy(clock_config.pass, server->pass);
server->post_recv=true;
}
}
//// LED Color
if (server->post_page == LED_COLOR_PAGE){
if (get_post_data_param(p, "ledcolor", clock_config.led_color)) {
server->post_recv=true;
}
}
if (server->post_page == NTP_SERVER_PAGE) {
if (get_post_data_param(p, "ntp_server", clock_config.ntp_server) &&
get_post_data_param(p, "ntp_interval", clock_config.ntp_interval) &&
get_post_data_param(p, "timezone_offset", clock_config.timezone) ) {
server->post_recv=true;
}
}
if (server->post_page == LOCAL_TIME_PAGE){
char tmpstr[21], buff[21];
if (get_post_data_param(p, "date_time", tmpstr)) {
int y,m,d,h,min;
datetime_t dt;
strcpy(buff, urldecode(tmpstr));
sscanf(buff, "%d-%d-%dT%d:%d", &y, &m,&d,&h,&min);
dt.year=y;
dt.month=m;
dt.day=d;
dt.hour=h;
dt.min=min;
dt.dotw=1;
if (!rtc_set_datetime(&dt)) printf("set local time error\n");
server->post_recv=true;
}
}
ret = ERR_OK;
} else {
ret = ERR_VAL;
}
/* this function must ALWAYS free the pbuf it is passed or it will leak memory */
pbuf_free(p);
return ret;
}
void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len) {
if (server->current_connection == connection) {
//if (server->valid_connection == connection) {
if (server->post_recv) {
switch (server->post_page) {
case WIFI_CONN_PAGE:
save_to_flash(true);
snprintf(response_uri, response_uri_len, "/wifi_conn.shtml");
break;
case NTP_SERVER_PAGE:
sntp_stop();
sntp_init();
display_clock_digits(true);
save_to_flash(false);
snprintf(response_uri, response_uri_len, "/index.shtml");
break;
case LED_COLOR_PAGE:
set_led_clock_color();
case LOCAL_TIME_PAGE:
display_clock_digits(true);
save_to_flash(false);
snprintf(response_uri, response_uri_len, "/index.shtml");
break;
}
}
//}
server->current_connection = NULL;
//server->valid_connection = NULL;
}
}
/*===== post end =====*/
/* === ssi begin =====*/
const char* __not_in_flash("httpd") ssi_tags[] = {
"scanwifi", //0
"ssid", //1
"ledcolor", //2
"wifistat", //3
"ltime", //4
"ntpserv", //5
"ntpint", //6
"timezone", //7
};
/* for scan wifi, multipart: every part for one scaned AP*/
u16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInsertLen, u16_t current_tag_part, u16_t *next_tag_part)
{
size_t printed;
static char buff[500];
char keyimg[]="<img src='img/key.png'>";
char checked[8]="checked";
datetime_t dt;
int rssi=1;
switch (iIndex) {
case 0: // for scanwifi in select_wifi.shtml
if (aps) {
if (current_tag_part < aps->len) {
if (((aps->AP)+current_tag_part)->rssi > -90) rssi=2;
if (((aps->AP)+current_tag_part)->rssi > -80) rssi=3;
if (((aps->AP)+current_tag_part)->rssi > -70) rssi=4;
if (((aps->AP)+current_tag_part)->auth_mode == CYW43_AUTH_OPEN) strcpy(keyimg,"");
if (current_tag_part == 0) strcpy(checked, "checked"); else strcpy(checked, "");
sprintf(buff, "<tr>"
"<td>"
"<input type='radio' name='ssid' %s value='%s'>%s"
"</td>"
"<td>"
"<img src='img/wifi%d.png'>"
"</td>"
"<td>%s</td></tr>",
checked, ((aps->AP)+current_tag_part)->ssid,((aps->AP)+current_tag_part)->ssid, rssi, keyimg);
*next_tag_part=current_tag_part+1;
printed = snprintf(pcInsert, iInsertLen, buff);
} else {
printed = snprintf(pcInsert, iInsertLen, "");
}
}
break;
case 1: // for ssid in wifi_conn.shtml
printed = snprintf(pcInsert, iInsertLen,server->ssid);
break;
case 2: // for led_color
printed = snprintf(pcInsert, iInsertLen,clock_config.led_color);
break;
case 3: // for wifi_state
if (clock_config.wifi_connected)
printed = snprintf(pcInsert, iInsertLen,"img/lan_connected.png");
else
printed = snprintf(pcInsert, iInsertLen,"img/lan_disconnected.png");
break;
case 4: // localtime
rtc_get_datetime(&dt);
sprintf(buff, "%04d-%02d-%02dT%02d:%02d", dt.year, dt.month,dt.day, dt.hour, dt.min);
printf("%s\n", buff);
printed = snprintf(pcInsert, iInsertLen,buff);
break;
case 5: // ntp server
printed = snprintf(pcInsert, iInsertLen,clock_config.ntp_server);
break;
case 6: //ntp interval
printed = snprintf(pcInsert, iInsertLen,clock_config.ntp_interval);
break;
case 7: //timezone
printed = snprintf(pcInsert, iInsertLen,clock_config.timezone);
break;
default:
printed = snprintf(pcInsert, iInsertLen, "");
}
return printed;
}
/* === ssi end =====*/
void ap_http_server_start() {
aps = (SCAN_APS_T*)calloc(1, sizeof(SCAN_APS_T));
if (!aps) {
printf("cannot alloc scan ap memory\n");
return;
}
server = (HTTP_POST_SERVER_T*) calloc(1, sizeof(HTTP_POST_SERVER_T));
if (!server) {
printf("cannot alloc server object\n");
return;
}
printf("Starting AP Mode: local IP:192.168.4.1\n");
cyw43_arch_enable_ap_mode(WIFI_AP_SSID, WIFI_AP_PASSWORD, CYW43_AUTH_WPA2_AES_PSK);
/* start dhcp server*/
ip_addr_t gw, mask;
IP4_ADDR(ip_2_ip4(&gw), 192, 168, 4, 1);
IP4_ADDR(ip_2_ip4(&mask), 255, 255, 255, 0);
dhcp_server_init(&dhcp_server, &gw, &mask);
scan_aps(aps, 20000);
//check tag length
size_t i;
for (i = 0; i < LWIP_ARRAYSIZE(ssi_tags); i++) {
LWIP_ASSERT("tag too long for LWIP_HTTPD_MAX_TAG_NAME_LEN",
strlen(ssi_tags[i]) <= LWIP_HTTPD_MAX_TAG_NAME_LEN);
}
http_set_ssi_handler(ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags));
http_set_cgi_handlers(cgi_handlers, LWIP_ARRAYSIZE(cgi_handlers));
httpd_init();
}
void ap_http_server_stop() {
dhcp_server_deinit(&dhcp_server);
free(aps);
cyw43_arch_deinit();
}
void http_server_start() {
aps = (SCAN_APS_T*)calloc(1, sizeof(SCAN_APS_T));
if (!aps) {
printf("cannot alloc scan ap memory\n");
return;
}
server = (HTTP_POST_SERVER_T*) calloc(1, sizeof(HTTP_POST_SERVER_T));
if (!server) {
printf("cannot alloc server object\n");
return;
}
scan_aps(aps, 20000);
//check tag length
size_t i;
for (i = 0; i < LWIP_ARRAYSIZE(ssi_tags); i++) {
LWIP_ASSERT("tag too long for LWIP_HTTPD_MAX_TAG_NAME_LEN",
strlen(ssi_tags[i]) <= LWIP_HTTPD_MAX_TAG_NAME_LEN);
}
http_set_ssi_handler(ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags));
http_set_cgi_handlers(cgi_handlers, LWIP_ARRAYSIZE(cgi_handlers));
httpd_init();
}
ap_http_server.h:
#ifndef __HTTP_SERVER_H_
#define __HTTP_SERVER_H_
#define FLASH_OFFSET 0x1FD000 //last 3*4k
#define FLASH_STORAGE_PAGES 2
#define WIFI_PASS_BUFSIZE 91
void ap_http_server_start();
void http_server_start();
void ap_http_server_stop();
bool read_config_from_flash();
char* urldecode(char* str);
#endif