From 94ea767285253302fa9361ff47e24453d9cf04e7 Mon Sep 17 00:00:00 2001 From: kenji Date: Fri, 30 May 2025 09:17:55 -0400 Subject: [PATCH] code clean up and added comments --- README.md | 15 ++++---- build/webkeyboard.uf2 | Bin 842752 -> 842752 bytes hid.c | 38 ++------------------- main.c | 7 +++- parse_keys.c | 26 ++++++++++---- parse_keys.h | 13 +++---- server.c | 77 ++++++++++++++++++++++++++++++------------ usb_descriptors.c | 21 ++++-------- 8 files changed, 104 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 458662f..aa40136 100644 --- a/README.md +++ b/README.md @@ -18,24 +18,24 @@ cable to the host computer or other device. On initial boot, the Raspberry Pi Pico W will enter Access Point mode with SSID "picokb" and password "password". Connect to this network with another -device and open a browser window to [http://picokb](http://picokb). +device and open a browser window to http://192.168.0.1. Go to the "Configure W-Fi" page and enter the SSID and password to the network that you would like the Raspberry Pi Pico W to connect. Optionally, also enter a hostname for the device and static IP information. Hit the "Save" button to save the network information to device flash. -Browse back to the main UI page at [http://picokb](http://picokb) and press +Browse back to the main UI page at http://192.168.0.1 and press "Reboot". The device should automatically connect to the network with the saved information. You can then connect to the device using a web browser at the device's assigned IP or hostname by accessing http://IP_ADDRESS or -http://HOSTNAME +http://HOSTNAME (http://picokb is no hostname is set). If the Pico W fails to connect to the saved Wi-Fi network, it will once again enter Access Point mode, and you can connect to the device directly to modify the network information. Alternatively, you can use the device directly in Access Point mode by -accessing the device at [http://picokb](http://picokb). The program is +accessing the device at http://192.168.0.1. The program is set to reboot automatically to try to reconnect to the nextwork after 10 minutes of inactivity if no keyboard input is received during that time. @@ -44,7 +44,7 @@ minutes of inactivity if no keyboard input is received during that time. This software is distributed under the [GNU General Public License version 3](LICENSE), with the exception of the libraries in the following section. -## Attributions +## Credits The project uses code from the following sources: @@ -53,5 +53,6 @@ The project uses code from the following sources: - [Raspberry Pi Pico SDK Examples](https://github.com/raspberrypi/pico-examples) for a modified version of [pico_w/wifi/lwipopts_examples_common.h](lwipopts.h) distributed under the BSD-3-Clause license -- [TinyUSB](https://github.com/hathach/tinyusb) for the base version of - [tusb_config.h](tusb_config.h) distributed under the MIT license +- [TinyUSB](https://github.com/hathach/tinyusb) for template versions of + [hid.c](hid.c), [tusb_config.h](tusb_config.h), and + [usb_descriptors.c/h](usb_descriptors.c) distributed under the MIT license diff --git a/build/webkeyboard.uf2 b/build/webkeyboard.uf2 index d6cde63b424fc4678f86efda258f1d71990e9d72..2550a56c30e6be7694920eae47b7ece9ff94b945 100644 GIT binary patch delta 73 zcmZqJVcf98c!QY)BYU&CM7y~JBM>tIG4pnF36__-OvVQ7N^UGb%nHP8K+F!r96-zo P#9TnkytIG4pnF36__-Ohy*%N^UGb%nHP8K+F!r96-zo P#9Tnky0) { @@ -84,13 +84,7 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { - // TODO not Implemented - (void) instance; - (void) report_id; - (void) report_type; - (void) buffer; - (void) reqlen; - + // echo USB communication to CDC for debugging char tempbuf[128]; size_t count; if(reqlen>0) { @@ -108,29 +102,3 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t return 0; } - - -// Invoked when sent REPORT successfully to host -// Application can use this to send the next report -// Note: For composite reports, report[0] is report ID -/* -void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len) -{ - (void) instance; - (void) len; - - char tempbuf[128]; - size_t count; - if(len>0) { - count = sprintf(tempbuf, "\nDevice sent report on interface %u (%u)\n", instance, len); - tud_cdc_write(tempbuf, count); - for(int i=0;ikey, key) == 0){ - //printf("key found"); return keycode->val; } } + // key not found in lookup table return 0x00; } +// parse modifier keys and turn them into the appropriate modifier bit code uint8_t parse_mod(char * key) { for (int i=0; i < NMODS; i++) { keycode_dict * modcode = &modtable[i]; if (strcmp(modcode->key, key) == 0){ - //printf("key found"); return modcode->val; } } diff --git a/parse_keys.h b/parse_keys.h index 33708a9..c7dee13 100644 --- a/parse_keys.h +++ b/parse_keys.h @@ -9,6 +9,8 @@ uint8_t parse_mod(char * key); typedef struct { char * key; uint8_t val; } keycode_dict; + +// modifier key bit codes #define KEY_MOD_LCTRL 0x01 #define KEY_MOD_LSHIFT 0x02 #define KEY_MOD_LALT 0x04 @@ -18,14 +20,7 @@ typedef struct { char * key; uint8_t val; } keycode_dict; #define KEY_MOD_RALT 0x40 #define KEY_MOD_RMETA 0x80 -/** - * Scan codes - last N slots in the HID report (usually 6). - * 0x00 if no key pressed. - * - * If more than N keys are pressed, the HID reports - * KEY_ERR_OVF in all slots to indicate this condition. - */ - +// list of USB scan codes for individual keys #define KEY_NONE 0x00 // No key pressed #define KEY_ERR_OVF 0x01 // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key") // 0x02 // Keyboard POST Fail @@ -277,6 +272,7 @@ typedef struct { char * key; uint8_t val; } keycode_dict; #define KEY_MEDIA_REFRESH 0xfa #define KEY_MEDIA_CALC 0xfb +// dictionary converting Javascript key code strings to USB scan codes static keycode_dict keytable[] = { {"KeyA", KEY_A}, {"KeyB", KEY_B}, @@ -376,6 +372,7 @@ static keycode_dict keytable[] = { {"WakeUp", KEY_POWER} }; +// dictionary for Javascript key code strings to USB keyboard modifier bits static keycode_dict modtable[] = { {"ControlLeft", KEY_MOD_LCTRL}, {"ShiftLeft", KEY_MOD_LSHIFT}, diff --git a/server.c b/server.c index a264bb1..8d4c272 100644 --- a/server.c +++ b/server.c @@ -26,6 +26,7 @@ static bool config_loaded = false; static bool bad_config = false; static bool reboot = false; +// list of SSI variable names for lwIP SSI handler const char * __not_in_flash("httpd") ssi_tags[] = { "num", "caps", @@ -56,48 +57,53 @@ static const tCGI cgi_handlers[] = { }; void run_http_server() { - //sleep_ms(5000); - if (cyw43_arch_init_with_country(CYW43_COUNTRY_USA)) { printf("failed to initialize\n"); return; } + // fall back to AP mode unless Wi-Fi connection is successful clientmode = false; + // read network config from flash config_loaded = net_config_load(&wifi); if ( config_loaded ){ printf("read config file\n"); cyw43_arch_enable_sta_mode(); - // set hostname - //cyw43_arch_lwip_begin(); + // set hostname if saved, otherwise use default setting struct netif *n = &cyw43_state.netif[CYW43_ITF_STA]; if (wifi.host) { netif_set_hostname(n, wifi.host); } else { netif_set_hostname(n, DEFAULTHOST); } + + // use manual IP address if configured if (wifi.manual) { dhcp_release_and_stop(n); netif_set_addr(n, &(wifi.ip), &(wifi.mask), &(wifi.gw)); + + // inform mDNS of hostname dhcp_inform(n); } netif_set_up(n); - //cyw43_arch_lwip_end(); + // attempt to connect to saved Wi-Fi network if(cyw43_arch_wifi_connect_timeout_ms(wifi.ssid, wifi.pass, CYW43_AUTH_WPA2_AES_PSK, 10000)){ printf("failed to connect to %s\n", wifi.ssid); } else{ + // Wi-Fi client connection successful clientmode = true; } } if (!clientmode) { - //start AP mode + // did not connect to Wi-Fi, launch AP mode cyw43_arch_enable_ap_mode(DEFAULTHOST, DEFAULTPASS, CYW43_AUTH_WPA2_AES_PSK); + // set AP address to 192.168.0.1 ip4_addr_t ip, mask, gw; IP4_ADDR(&ip, 192, 168, 0, 1); IP4_ADDR(&mask, 255, 255, 255, 0); @@ -105,12 +111,13 @@ void run_http_server() { netif_set_ipaddr(netif_default, &ip); netif_set_up(netif_default); + // init DHCP server to hand out addresses to connected clients dhcp_server_t dhcp; dhcp_server_init(&dhcp, &gw, &mask); printf("launched in AP mode\n"); } - // start the server + // start the HTTP web server for keyboard input and config page httpd_init(); http_set_cgi_handlers(cgi_handlers, 3); for (size_t i = 0; i < LWIP_ARRAYSIZE(ssi_tags); i++) { @@ -120,14 +127,19 @@ void run_http_server() { http_set_ssi_handler(ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags)); printf("HTTP server initialized\n"); + // start a watchdog timer with 8 seconds so it can reboot if it disconnects watchdog_enable(8000,1); if (clientmode) { + // signal that device is in client mode with solid LED on cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN,1); while(true) { if ( absolute_time_diff_us(lastCheck, get_absolute_time()) > 5000000) { + // check if Wi-Fi is still connected every 5 seconds and + // feed the watchdog timer if it is to prevent reboot if ( cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA) == 3 ) { lastCheck = get_absolute_time(); + // update watchdog if reboot has not been requested if(!reboot){ watchdog_update(); } @@ -139,12 +151,16 @@ void run_http_server() { static int led = 1; while(true) { if (absolute_time_diff_us(lastCheck, get_absolute_time()) > 1000000) { + // signal device is in AP mode with blinking LED led = !led; cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led); lastCheck = get_absolute_time(); if ( config_loaded && (absolute_time_diff_us(lastActive, lastCheck) > 600000000)) { + // if there is no keyboard activity for 10 minutes + // reboot to try to (re)connect to Wi-Fi reboot = true; } + // update watchdog unless reboot has been requested if(!reboot) { watchdog_update(); } @@ -153,10 +169,11 @@ void run_http_server() { } } +// lwIP cgi handler for GET requests to sendkeys.cgi const char * sendkeys_cgi (int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { for (int i=0; i < iNumParams; i++) { + // search GET parameters for keys and forward for processing to USB if (strcmp(pcParam[i], "keys") == 0 ) { - //printf("Web input: %s\n", pcValue[i]); parse_key_list(pcValue[i]); } } @@ -167,6 +184,7 @@ const char * sendkeys_cgi (int iIndex, int iNumParams, char *pcParam[], char *pc return "/success.html"; } +// lwIP cgi handler for form submission of network config to wifi.cgi const char * save_wifi (int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { // clear all values in manual network settings memset(ip, 0, sizeof(ip)); @@ -176,6 +194,7 @@ const char * save_wifi (int iIndex, int iNumParams, char *pcParam[], char *pcVal bad_config = false; wifi.manual = true; + // read parameter values for (int i=0; i < iNumParams; i++) { if (strcmp(pcParam[i], "ssid") == 0){ if (pcValue[i][0] == "\0") { @@ -217,8 +236,10 @@ const char * save_wifi (int iIndex, int iNumParams, char *pcParam[], char *pcVal } if (bad_config) { + // config is invalid, so don't save and return to config page return "/wifi.shtml"; } else { + // config is valid, prepare for saving to flash IP4_ADDR(&(wifi.ip), ip[0], ip[1], ip[2], ip[3]); if(!wifi.ip.addr) { wifi.ip.addr = IPADDR_NONE; @@ -237,6 +258,7 @@ const char * save_wifi (int iIndex, int iNumParams, char *pcParam[], char *pcVal watchdog_update(); + // save configuratitons to flash net_config_write(&wifi); printf("wifi settings saved\n"); @@ -245,7 +267,9 @@ const char * save_wifi (int iIndex, int iNumParams, char *pcParam[], char *pcVal } } +// lwIP cgi handler for reboots initiated from the web const char * reboot_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { + // turn on reboot flag to prevent watchdog from updating reboot = true; return "/success.html"; @@ -253,20 +277,28 @@ const char * reboot_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcVal void net_config_write(net_config *config) { + // core 0 must have its TinyUSB interrupts disabled to allow flash writes multicore_lockout_start_blocking(); + // blank out memory for staging config save uint8_t buf[FLASH_PAGE_SIZE]; memset(buf, 0x00, FLASH_PAGE_SIZE); memcpy(buf, config, sizeof(net_config)); + // turn off core 1 web server interrupts to allow flash writes uint32_t interrupts = save_and_disable_interrupts(); - flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE); - flash_range_program(FLASH_TARGET_OFFSET, buf, FLASH_PAGE_SIZE); - restore_interrupts(interrupts); + // erase page where save will go in flash + flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE); + // write new config to flash + flash_range_program(FLASH_TARGET_OFFSET, buf, FLASH_PAGE_SIZE); + + // restore interrupts on both core 1 and core 0 + restore_interrupts(interrupts); multicore_lockout_end_blocking(); } +// read network config from flash and loads to program memory bool net_config_load(net_config *config) { const uint8_t *data = (const uint8_t *) (XIP_BASE+FLASH_TARGET_OFFSET); memcpy(config, data, sizeof(net_config)); @@ -274,45 +306,46 @@ bool net_config_load(net_config *config) { return ( (config->header == STARTFILE) && (config->footer == ENDFILE) ); } +// lwIP SSI handler for loading variables into HTML pages uint16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInsertLen) { size_t printed; switch (iIndex) { - case 0: //num + case 0: //num - numlock status if (led_code & 1) { printed = snprintf(pcInsert, iInsertLen, "on"); } else { printed = snprintf(pcInsert, iInsertLen, "off"); } break; - case 1: //caps + case 1: //caps - capslock status if (led_code & 2) { printed = snprintf(pcInsert, iInsertLen, "on"); } else { printed = snprintf(pcInsert, iInsertLen, "off"); } break; - case 2: //scroll + case 2: //scroll - scrollock status if (led_code & 4) { printed = snprintf(pcInsert, iInsertLen, "on"); } else { printed = snprintf(pcInsert, iInsertLen, "off"); } break; - case 3: //ssid + case 3: //ssid - network config SSID if (config_loaded && wifi.ssid) { printed = snprintf(pcInsert, iInsertLen, "value=\"%s\"", wifi.ssid); } else { printed = 0; } break; - case 4: //pass + case 4: //pass - network config password if (config_loaded && wifi.pass) { printed = snprintf(pcInsert, iInsertLen, "value=\"%s\"", wifi.pass); } else { printed = 0; } break; - case 5: //host + case 5: //host - network config hostname if (config_loaded && wifi.host) { printed = snprintf(pcInsert, iInsertLen, "value=\"%s\"", wifi.host); } else { @@ -322,7 +355,7 @@ uint16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInse case 6: case 7: case 8: - case 9: //ip0-3 + case 9: //ip0-3 - network config manual IP address parts if (config_loaded && wifi.ip.addr) { printed = snprintf(pcInsert, iInsertLen, "value=\"%d\"", ip4_addr_get_byte(&(wifi.ip), iIndex-6)); } else { @@ -332,7 +365,7 @@ uint16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInse case 10: case 11: case 12: - case 13: //mask0-3 + case 13: //mask0-3 - network config manual netmask parts if (config_loaded && wifi.mask.addr) { printed = snprintf(pcInsert, iInsertLen, "value=\"%d\"", ip4_addr_get_byte(&(wifi.mask), iIndex-10)); } else { @@ -342,14 +375,14 @@ uint16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInse case 14: case 15: case 16: - case 17: //gw0-3 + case 17: //gw0-3 - network config manual gateway parts if (config_loaded && wifi.gw.addr) { printed = snprintf(pcInsert, iInsertLen, "value=\"%d\"", ip4_addr_get_byte(&(wifi.gw), iIndex-14)); } else { printed = 0; } break; - case 18: // dhcp + case 18: // dhcp - network config DHCP client enabled/disabled if ((!config_loaded) || !(wifi.manual) ){ printed = snprintf(pcInsert, iInsertLen, "checked"); } else { @@ -364,10 +397,12 @@ uint16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInse return (uint16_t)printed; } +// save keyboard's LED indicator status to memory void set_indicator(uint8_t const* buffer) { led_code = *buffer; } +// turn URL-formatted string from GET request and turn into regular string void urldecode(char *urlstring, char *decoded) { uint8_t conv; while(*urlstring) { diff --git a/usb_descriptors.c b/usb_descriptors.c index ae477c5..6f441c3 100644 --- a/usb_descriptors.c +++ b/usb_descriptors.c @@ -123,11 +123,11 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) char const* string_desc_arr [] = { (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) - "Raspberry Pi", // 1: Manufacturer - "Pico Web Keyboard", // 2: Product - "1234567890123456789", // 3: Serials, should use chip ID - "Pico Web Keyboard CDC", // 4: CC Interface - "TinyUSB Keyboard", // 5: HID Keyboard Interface + "Raspberry Pi", // 1: Manufacturer + "Pico Web Keyboard", // 2: Product + "1234567890123456789", // 3: Serials, should use chip ID + "Pico Web Keyboard CDC", // 4: CDC Interface + "TinyUSB Keyboard", // 5: HID Keyboard Interface }; static uint16_t _desc_str[32]; @@ -181,15 +181,6 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) { (void) itf; - //switch(itf) { - // case 0: - return desc_hid_report; - // case 1: - // return desc_nkro_report; - // case 2: - // return desc_media_report; - // default: - // return 0; - //} + return desc_hid_report; }