/* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** CuVoodoo clapperboard firmware (for STM32F103Cx micro-controller) * @file main.c * @author King Kévin * @date 2016-2018 */ /* standard libraries */ #include // standard integer types #include // boolean type #include // string utilities /* STM32 (including CM3) libraries */ #include // Cortex M3 utilities #include // vector table definition #include // interrupt utilities #include // general purpose input output library #include // real-time control clock library #include // external interrupt utilities #include // real time clock utilities #include // independent watchdog utilities #include // debug utilities #include // flash utilities #include // timer utilities /* own libraries */ #include "global.h" // board definitions #include "print.h" // printing utilities #include "usart.h" // USART utilities #include "usb_cdcacm.h" // USB CDC ACM utilities #include "rtc_ds1307.h" // DS1307 RTC utilities #include "led_tm1637.h" // TM1637 7-segment controller utilities #include "led_max7219.h" // MAX7219 7-segment controller utilities #define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */ /** @defgroup main_flags flag set in interrupts to be processed in main loop * @{ */ volatile bool rtc_tick_flag = false; /**< flag set when RTC ticked */ volatile bool frame_flag = false; /**< flag set when a frame has passed */ volatile bool keep_alive_flag = false; /**< flag to restart shutdown counter on power switch activity */ volatile bool clap_flag = false; /**< flag set on clap */ /** @} */ #define POWER_SWITCH_PORT B /**< port to switch power of all devices (including this micro-controller) */ #define POWER_SWITCH_PIN 8 /**< pin to switch power of all devices (including this micro-controller) */ #define POWER_BUTTON_PORT B /**< port to detect power switching activity by shaking (to keep alive) */ #define POWER_BUTTON_PIN 1 /**< pin to detect power switching activity by shaking (to keep alive) */ #define SQUARE_WAVE_PORT B /**< port connected to RTC DS1307 square wave output */ #define SQUARE_WAVE_PIN 0 /**< pin connected to RTC DS1307 square wave output */ volatile uint8_t rtc_seconds = 0; /**< number of seconds passed, incremented by the square wave */ #define STANDBY_TIMEOUT 30 /**< number of seconds after last shake before going down */ volatile uint16_t standby_timer = 0; /**< number of seconds since last power-up activity (using shake sensor) */ #define FRAME_TIMER 2 /**< timer to count frame time */ #define FRAME_RATE 25 /**< frame rate in frames per second */ volatile uint8_t frame_count = 0; /**< number of frames passed */ #define BUZZER_TIMER 1 /**< timer to generate scene and take count */ #define BUZZER_1_PORT A /**< use timer channel 1 (and it's negative) to driver buzzer */ #define BUZZER_1_PIN 7 /**< use timer channel 1 (and it's negative) to driver buzzer */ #define BUZZER_2_PORT A /**< use timer channel 1 (and it's negative) to driver buzzer */ #define BUZZER_2_PIN 8 /**< use timer channel 1 (and it's negative) to driver buzzer */ #define MUX_EN_PORT B /**< port to enable multiplexer */ #define MUX_EN_PIN 9 /**< pin to enable multiplexer */ #define MUX_S0_PORT B /**< port to select multiplexer output */ #define MUX_S0_PIN 3 /**< pin to select multiplexer output */ #define MUX_S1_PORT B /**< port to select multiplexer output */ #define MUX_S1_PIN 4 /**< pin to select multiplexer output */ #define MUX_S2_PORT B /**< port to select multiplexer output */ #define MUX_S2_PIN 5 /**< pin to select multiplexer output */ /** number to be display and changed using the buttons */ struct number_t { uint16_t number; /**< number to display (0-9999, 0xffff=off) */ uint8_t up; /**< which of the 16 buttons is to increment the number */ uint8_t down; /**< which of the 16 buttons is to decrement the number */ uint8_t display; /**< which display on the channel multiplexer */ }; /** episode, scene, take, video 1, audio 1, video 2, audio 2 number (in this order) */ struct number_t numbers[] = { {0, 0, 4, 4}, // episode {0, 8, 12, 5}, // scene {0, 1, 5, 6}, // take {0, 2, 6, 3}, // video 1 {0, 10, 14, 2}, // audio 1 {0, 3, 7, 1}, // video 2 {0, 11, 15, 0}, // audio 2 }; #define BUTTON_SECONDS_UP 9 /**< which of the 16 buttons is to increment the seconds */ #define BUTTON_SECONDS_DOWN 13 /**< which of the 16 buttons is to decrement the seconds */ // buttons to adjust numbers and seconds (mutliplexed in a 4x4 matrix) #define BUTTONS_DRIVE_PORT A /**< port used to drive the buttons rows */ #define BUTTONS_DRIVE_PIN0 0 /**< pin used to drive buttons row 0 */ #define BUTTONS_DRIVE_PIN1 1 /**< pin used to drive buttons row 1 */ #define BUTTONS_DRIVE_PIN2 2 /**< pin used to drive buttons row 2 */ #define BUTTONS_DRIVE_PIN3 3 /**< pin used to drive buttons row 3 */ #define BUTTONS_READ_PORT A /**< port used to read the buttons columns */ #define BUTTONS_READ_PIN0 4 /**< pin used to read buttons column 0 */ #define BUTTONS_READ_PIN1 5 /**< pin used to read buttons column 1 */ #define BUTTONS_READ_PIN2 6 /**< pin used to read buttons column 2 */ #define BUTTONS_READ_PIN3 15 /**< pin used to read buttons column 3 */ #define CLAP_BUTTON_PORT B /**< port for button to detect clap action */ #define CLAP_BUTTON_PIN 14 /**< port for button to detect clap action */ /** Morse code variables, to buzz/beep scene and take */ #define MORSE_DOT 1 /**< unit Morse code duration in frames */ uint8_t morse[2*4*5*2] = {0}; /**< to encode 2 4-digit numbers (scene and take) into Morse code (5 sign+space) */ uint8_t morse_i = 0; /**< index in Morse array */ int16_t morse_delay = -1; /**< number of frames before beeping (-1=no need to beep yet, -2=beeping completed) */ /** user input command */ static char command[32] = {0}; /** user input command index */ uint8_t command_i = 0; size_t putc(char c) { size_t length = 0; // number of characters printed static char newline = 0; // to remember on which character we sent the newline if (0==c) { length = 0; // don't print string termination character } else if ('\r' == c || '\n' == c) { // send CR+LF newline for most carriage return and line feed combination if (0==newline || c==newline) { // send newline only if not already send (and only once on \r\n or \n\r) usart_putchar_nonblocking('\r'); // send CR over USART cdcacm_putchar('\r'); // send CR over USB usart_putchar_nonblocking('\n'); // send LF over USART cdcacm_putchar('\n'); // send LF over USB length += 2; // remember we printed 2 characters newline = c; // remember on which character we sent the newline } else { length = 0; // the \r or \n of \n\r or \r\n has already been printed } } else { usart_putchar_nonblocking(c); // send byte over USART cdcacm_putchar(c); // send byte over USB newline = 0; // clear new line length++; // remember we printed 1 character } return length; // return number of characters printed } /** process user command * @param[in] str user command string (\0 ended) */ static void process_command(char* str) { // split command const char* delimiter = " "; char* word = strtok(str,delimiter); if (!word) { goto error; } // parse command if (0==strcmp(word,"help")) { printf("available commands:\n"); printf("led [on|off|toggle]\n"); printf("time [HH:MM:SS]\n"); printf("date [YYYY-MM-DD]\n"); } else if (0==strcmp(word,"led")) { word = strtok(NULL,delimiter); if (!word) { goto error; } else if (0==strcmp(word,"on")) { led_on(); // switch LED on printf("LED switched on\n"); // notify user } else if (0==strcmp(word,"off")) { led_off(); // switch LED off printf("LED switched off\n"); // notify user } else if (0==strcmp(word,"toggle")) { led_toggle(); // toggle LED printf("LED toggled\n"); // notify user } else { goto error; } } else if (0==strcmp(word,"time")) { word = strtok(NULL,delimiter); if (!word) { printf("current time: %02u:%02u:%02u\n", rtc_ds1307_read_hours(), rtc_ds1307_read_minutes(), rtc_ds1307_read_seconds()); // get and print time from external RTC } else if (strlen(word)!=8 || word[0]<'0' || word[0]>'2' || word[1]<'0' || word[1]>'9' || word[3]<'0' || word[3]>'5' || word[4]<'0' || word[4]>'9' || word[6]<'0' || word[6]>'5' || word[7]<'0' || word[7]>'9') { // time format is incorrect goto error; } else { if (!rtc_ds1307_write_hours((word[0]-'0')*10+(word[1]-'0')*1)) { printf("setting hours failed\n"); } else if (!rtc_ds1307_write_minutes((word[3]-'0')*10+(word[4]-'0')*1)) { printf("setting minutes failed\n"); } else if (!rtc_ds1307_write_seconds((word[6]-'0')*10+(word[7]-'0')*1)) { printf("setting seconds failed\n"); } else { rtc_ds1307_oscillator_enable(); // be sure the oscillation is enabled printf("time set\n"); } } } else if (0==strcmp(word,"date")) { word = strtok(NULL,delimiter); if (!word) { printf("current date: 20%02u-%02u-%02u\n", rtc_ds1307_read_year(), rtc_ds1307_read_month(), rtc_ds1307_read_date()); } else if (strlen(word)!=10 || word[0]!='2' || word[1]!='0' || word[2]<'0' || word[2]>'9' || word[3]<'0' || word[3]>'9' || word[5]<'0' || word[5]>'1' || word[6]<'0' || word[6]>'9' || word[8]<'0' || word[8]>'3' || word[9]<'0' || word[9]>'9') { goto error; } else { if (!rtc_ds1307_write_year((word[2]-'0')*10+(word[3]-'0')*1)) { printf("setting year failed\n"); } else if (!rtc_ds1307_write_month((word[5]-'0')*10+(word[6]-'0')*1)) { printf("setting month failed\n"); } else if (!rtc_ds1307_write_date((word[8]-'0')*10+(word[9]-'0')*1)) { printf("setting day failed\n"); } else { printf("date set\n"); } } } else { goto error; } return; // command successfully processed error: printf("command not recognized. enter help to list commands\n"); return; } /** select clock output for TM1637 display using the analog multiplexer * @param[in] output clock output channel */ static void mux_select(uint8_t output) { if (output>7) { // multiplexer is only controlling 8 outputs return; } gpio_clear(GPIO(MUX_EN_PORT), GPIO(MUX_EN_PIN)); // enable multiplexer switch (output) { case 0: // output on channel C0 gpio_clear(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN)); gpio_clear(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN)); gpio_clear(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN)); break; case 1: // output on channel C1 gpio_set(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN)); gpio_clear(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN)); gpio_clear(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN)); break; case 2: // output on channel C2 gpio_clear(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN)); gpio_set(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN)); gpio_clear(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN)); break; case 3: // output on channel C3 gpio_set(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN)); gpio_set(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN)); gpio_clear(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN)); break; case 4: // output on channel C4 gpio_clear(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN)); gpio_clear(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN)); gpio_set(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN)); break; case 5: // output on channel C5 gpio_set(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN)); gpio_clear(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN)); gpio_set(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN)); break; case 6: // output on channel C6 gpio_clear(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN)); gpio_set(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN)); gpio_set(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN)); break; case 7: // output on channel C7 gpio_set(GPIO(MUX_S0_PORT), GPIO(MUX_S0_PIN)); gpio_set(GPIO(MUX_S1_PORT), GPIO(MUX_S1_PIN)); gpio_set(GPIO(MUX_S2_PORT), GPIO(MUX_S2_PIN)); break; default: break; } } /** encode scene and take into Morse code (in morse variable) */ static void encode_morse(void) { // numbers (scene and take) to encode in Morse code uint16_t morse_numbers[] = {numbers[1].number, numbers[2].number}; printf("morsing scene: %u, take: %u\n", morse_numbers[0], morse_numbers[1]); for (uint8_t morse_number=0; morse_number0) { // there is a command to process command[command_i] = 0; // end string command_i = 0; // prepare for next command process_command(command); // process user command } } else { // save user command input command[command_i] = c; // save command input if (command_i0) { // not ready to Morse morse_delay--; // still wait before Morsing } else if (0==morse_delay) { // ready to Morse while (morse_i=LENGTH(morse)) { // all codes done morse_delay = -2; // Morse completed // switch buzzer off by removing the output from the PWM gpio_clear(GPIO(BUZZER_1_PORT), GPIO(BUZZER_1_PIN)); gpio_set_mode(GPIO(BUZZER_1_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUZZER_1_PIN)); gpio_clear(GPIO(BUZZER_2_PORT), GPIO(BUZZER_2_PIN)); gpio_set_mode(GPIO(BUZZER_2_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUZZER_2_PIN)); } } // read button inputs (drive and read each row since they are multiplexed) uint16_t buttons_new = 0; // to save new button states // drive row 0 gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUTTONS_DRIVE_PIN0)); gpio_set(GPIO(BUTTONS_DRIVE_PORT), GPIO(BUTTONS_DRIVE_PIN0)); gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUTTONS_DRIVE_PIN1)|GPIO(BUTTONS_DRIVE_PIN2)|GPIO(BUTTONS_DRIVE_PIN3)); // read all columns for this row buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN0)) ? 1 : 0)<<0); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN1)) ? 1 : 0)<<1); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN2)) ? 1 : 0)<<2); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN3)) ? 1 : 0)<<3); // drive row 1 gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUTTONS_DRIVE_PIN1)); gpio_set(GPIO(BUTTONS_DRIVE_PORT), GPIO(BUTTONS_DRIVE_PIN1)); gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUTTONS_DRIVE_PIN0)|GPIO(BUTTONS_DRIVE_PIN2)|GPIO(BUTTONS_DRIVE_PIN3)); // read all columns for this row buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN0)) ? 1 : 0)<<4); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN1)) ? 1 : 0)<<5); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN2)) ? 1 : 0)<<6); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN3)) ? 1 : 0)<<7); // drive row 2 gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUTTONS_DRIVE_PIN2)); gpio_set(GPIO(BUTTONS_DRIVE_PORT), GPIO(BUTTONS_DRIVE_PIN2)); gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUTTONS_DRIVE_PIN0)|GPIO(BUTTONS_DRIVE_PIN1)|GPIO(BUTTONS_DRIVE_PIN3)); // read all columns for this row buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN0)) ? 1 : 0)<<8); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN1)) ? 1 : 0)<<9); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN2)) ? 1 : 0)<<10); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN3)) ? 1 : 0)<<11); // drive row 3 gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUTTONS_DRIVE_PIN3)); gpio_set(GPIO(BUTTONS_DRIVE_PORT), GPIO(BUTTONS_DRIVE_PIN3)); gpio_set_mode(GPIO(BUTTONS_DRIVE_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(BUTTONS_DRIVE_PIN0)|GPIO(BUTTONS_DRIVE_PIN1)|GPIO(BUTTONS_DRIVE_PIN2)); // read all columns for this row buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN0)) ? 1 : 0)<<12); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN1)) ? 1 : 0)<<13); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN2)) ? 1 : 0)<<14); buttons_new |= ((gpio_get(GPIO(BUTTONS_READ_PORT), GPIO(BUTTONS_READ_PIN3)) ? 1 : 0)<<15); uint16_t buttons_diff = buttons^buttons_new; // get difference buttons = buttons_new; // save new state if (buttons_diff) { // only do something if there is a change printf("button pressed: %016b\n", buttons); for (uint8_t number=0; number>8, numbers[number].number}; if (!rtc_ds1307_write_rom(number*LENGTH(bytes), bytes, LENGTH(bytes))) { printf("could not set number on EEPROM\n"); } } } if ((buttons_diff&(1<=60) { rtc_ds1307_time[0] -= 60; rtc_ds1307_time[1] += 1; } while (rtc_ds1307_time[1]>=60) { rtc_ds1307_time[1] -= 60; rtc_ds1307_time[2] += 1; } while (rtc_ds1307_time[2]>=24) { rtc_ds1307_time[2] -= 24; } } } else if (buttons_diff&(1<0) { rtc_ds1307_time[0] -= 1; } else { rtc_ds1307_time[0] = 59; if (rtc_ds1307_time[1]>0) { rtc_ds1307_time[1] -= 1; } else { rtc_ds1307_time[1] = 59; rtc_ds1307_time[2] -= 1; // it can't be 0 } } } } rtc_seconds = rtc_ds1307_time[0]; // save seconds since this is only updated every minute if (!rtc_ds1307_write_time(rtc_ds1307_time[0], rtc_ds1307_time[1], rtc_ds1307_time[2], rtc_ds1307_time[3], rtc_ds1307_time[4], rtc_ds1307_time[5], rtc_ds1307_time[6])) { // save adjusted time to DS1307 printf("could not set time on DS1307: resetting\n"); rtc_ds1307_setup(); // resetting peripheral } } } standby_timer = 0; // restart standby timer } } while (rtc_tick_flag) { // the external RTC ticked rtc_tick_flag = false; // reset flag action = true; // action has been performed led_toggle(); // toggle LED (good to indicate if main function is stuck) if (standby_timer>=STANDBY_TIMEOUT) { // standby timeout complete // go into standby mode printf("shutting down\n"); // increment and save take and video/audio numbers if there has been a clap if (-2==morse_delay) { morse_delay = -1; // prevent incrementing multiple times uint8_t bytes[LENGTH(numbers)*2] = {0}; // all numbers to write again in EEPROM for (uint8_t number=0; number>8; // set number to save bytes[2*number+1] = numbers[number].number; // set number to save } if (!rtc_ds1307_write_rom(0, bytes, LENGTH(bytes))) { // save numbers to EEPROM printf("could not set numbers on EEPROM\n"); } } for (uint8_t tm1637=0; tm1637<7; tm1637++) { // switch off TM1637 displays mux_select(tm1637); // selecting TM1637 display led_tm1637_off(); // switch off TM1637 display } led_max7219_off(0xff); // switch off MAX7219 displays timer_disable_counter(TIM(BUZZER_TIMER)); // stop buzzing gpio_clear(GPIO(POWER_SWITCH_PORT), GPIO(POWER_SWITCH_PIN)); // switch power of by disconnecting from battery SCB_SCR |= SCB_SCR_SLEEPDEEP; // enable deep sleep pwr_set_standby_mode(); // go to deep sleep while (true); // we should be shut down at this point } if (rtc_seconds>=60) { // one minute passed rtc_ds1307_time = rtc_ds1307_read_time(); // get time/date from external RTC if (rtc_ds1307_time==NULL) { printf("could not get time from DS1307: resetting\n"); rtc_ds1307_setup(); // resetting peripheral } else { rtc_seconds = rtc_ds1307_time[0]; // get actual number of seconds if (0==rtc_ds1307_time[1] && 0==rtc_ds1307_time[2]) { // new day arrived led_max7219_number(20000000+rtc_ds1307_time[6]*10000+rtc_ds1307_time[5]*100+rtc_ds1307_time[4], 0x14, 0); // display date on 1st display } } } } while (keep_alive_flag) { // power switch is detecting movement to keep clapperboard running action = true; // action has been performed keep_alive_flag = false; // clear flag standby_timer = 0; // restart standby timer } while (clap_flag) { // clap detected action = true; // action has been performed clap_flag = false; // clear flag if (morse_delay<0) { // if beeping is not already queued encode_morse(); // encode scene and take number into Morse code morse_delay = FRAME_RATE; // wait 1 second } } if (action) { // go to sleep if nothing had to be done, else recheck for activity action = false; } else { __WFI(); // go to sleep and wait for interrupt } } // main loop } /** RTC square wave input ISR to synchronize to seconds and count them */ void EXTI_ISR(SQUARE_WAVE_PIN)(void) { exti_reset_request(EXTI(SQUARE_WAVE_PIN)); // reset interrupt frame_count = 0; // re-sync frame counter to second rtc_seconds++; // increment number of seconds passed if (standby_timer