diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c44f51a..4f2d291 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ file(RENAME fsdata.c my_fsdata.c) add_executable(${PROGRAM_NAME} main.cpp + double_reset_detector.cpp ) target_compile_definitions(${PROGRAM_NAME} PRIVATE WIFI_SSID=\"${WIFI_SSID}\" diff --git a/src/double_reset_detector.cpp b/src/double_reset_detector.cpp new file mode 100644 index 0000000..2c37801 --- /dev/null +++ b/src/double_reset_detector.cpp @@ -0,0 +1,58 @@ +// Double reset logic based on pico_bootsel_via_double_reset.c from pico sdk + +#include "pico.h" +#include "pico/time.h" +#include "pico/bootrom.h" +#include "pico/binary_info.h" +#include "pico/stdlib.h" + +namespace double_reset_detector { + +bool double_reset_detected = false; + +// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS, Window of opportunity for a second press of a reset button to enter BOOTSEL mode (milliseconds), type=int, default=200, group=pico_bootsel_via_double_reset +#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS +#define PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS 500 +#endif + +// Doesn't make any sense for a RAM only binary +#if !PICO_NO_FLASH +static const uint32_t magic_token[] = { + 0xf01681de, 0xbd729b29, 0xd359be7a, +}; + +static uint32_t __uninitialized_ram(magic_location)[count_of(magic_token)]; + +/* Check for double reset and enter AP mode if detected + * + * This function is registered to run automatically before main(). The + * algorithm is: + * + * 1. Check for magic token in memory; enter AP mode if found. + * 2. Initialise that memory with that magic token. + * 3. Do nothing for a short while (few hundred ms). + * 4. Clear the magic token. + * 5. Continue with normal boot. + * + * Resetting the device twice quickly will interrupt step 3, leaving the token + * in place so that the second boot will go to the bootloader. + */ +static void __attribute__((constructor)) boot_double_tap_check(void) { + for (uint i = 0; i < count_of(magic_token); i++) { + if (magic_location[i] != magic_token[i]) { + // Arm, wait, then disarm and continue booting + for (i = 0; i < count_of(magic_token); i++) { + magic_location[i] = magic_token[i]; + } + busy_wait_us(PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS * 1000); + magic_location[0] = 0; + return; + } + } + // Detected a double reset + magic_location[0] = 0; + double_reset_detected = true; +} + +#endif +} // namespace double_reset_detector \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 694cf36..75de67d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,13 +2,41 @@ #include #include "lwip/apps/httpd.h" +#include "lwip/err.h" #include "pico/stdlib.h" #include "pico/cyw43_arch.h" #include "lwipopts.h" #include "ssi.h" +namespace double_reset_detector { + extern bool double_reset_detected; +} + +int connect_to_wifi() { + cyw43_arch_enable_sta_mode(); + // this seems to be the best be can do using the predefined `cyw43_pm_value` macro: + // cyw43_wifi_pm(&cyw43_state, CYW43_PERFORMANCE_PM); + // however it doesn't use the `CYW43_NO_POWERSAVE_MODE` value, so we do this instead: + cyw43_wifi_pm(&cyw43_state, cyw43_pm_value(CYW43_NO_POWERSAVE_MODE, 20, 1, 1, 1)); + + 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"); + return ERR_CONN; + } else { + printf("Connected.\n"); + + extern cyw43_t cyw43_state; + auto ip_addr = cyw43_state.netif[CYW43_ITF_STA].ip_addr.addr; + printf("IP Address: %lu.%lu.%lu.%lu\n", ip_addr & 0xFF, (ip_addr >> 8) & 0xFF, (ip_addr >> 16) & 0xFF, ip_addr >> 24); + } + return ERR_OK; +} void run_server() { + if (connect_to_wifi() != ERR_OK){ + return; + } httpd_init(); ssi_init(); printf("Http server initialized.\n"); @@ -16,6 +44,19 @@ void run_server() { for (;;) {} } +void setup() { + if (double_reset_detector::double_reset_detected) { + printf("Double reset detected!"); + // just blink the LED for now + while (true) { + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); + sleep_ms(500); + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); + sleep_ms(500); + } + } +} + int main() { stdio_init_all(); @@ -25,24 +66,6 @@ int main() { } // turn on LED to distinguish from BOOTSEL mode cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); - - cyw43_arch_enable_sta_mode(); - // this seems to be the best be can do using the predefined `cyw43_pm_value` macro: - // cyw43_wifi_pm(&cyw43_state, CYW43_PERFORMANCE_PM); - // however it doesn't use the `CYW43_NO_POWERSAVE_MODE` value, so we do this instead: - cyw43_wifi_pm(&cyw43_state, cyw43_pm_value(CYW43_NO_POWERSAVE_MODE, 20, 1, 1, 1)); - - 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"); - return 1; - } else { - printf("Connected.\n"); - - extern cyw43_t cyw43_state; - auto ip_addr = cyw43_state.netif[CYW43_ITF_STA].ip_addr.addr; - printf("IP Address: %lu.%lu.%lu.%lu\n", ip_addr & 0xFF, (ip_addr >> 8) & 0xFF, (ip_addr >> 16) & 0xFF, ip_addr >> 24); - } - + setup(); run_server(); } \ No newline at end of file