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..43c94b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,26 +7,12 @@ #include "lwipopts.h" #include "ssi.h" - -void run_server() { - httpd_init(); - ssi_init(); - printf("Http server initialized.\n"); - // infinite loop for now - for (;;) {} +namespace double_reset_detector { + extern bool double_reset_detected; } -int main() { - stdio_init_all(); - - if (cyw43_arch_init()) { - printf("failed to initialise\n"); - return 1; - } - // turn on LED to distinguish from BOOTSEL mode - cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); - - cyw43_arch_enable_sta_mode(); +void 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: @@ -43,6 +29,39 @@ int main() { 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); } - +} + +void run_server() { + connect_to_wifi(); + httpd_init(); + ssi_init(); + printf("Http server initialized.\n"); + // infinite loop for now + 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(); + + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + // turn on LED to distinguish from BOOTSEL mode + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); + setup(); run_server(); } \ No newline at end of file