2016-01-17 14:54:54 +01:00
/* 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 < http : //www.gnu.org/licenses/>.
*
*/
2020-03-06 11:11:53 +01:00
/** dachboden front panel access control
2020-02-17 18:08:17 +01:00
* @ file
2016-08-14 21:02:38 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-02-17 18:08:17 +01:00
* @ date 2016 - 2020
2016-08-14 21:02:38 +02:00
*/
2016-01-17 14:54:54 +01:00
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // standard utilities
2016-08-14 21:02:38 +02:00
# include <string.h> // string utilities
2019-06-18 17:43:43 +02:00
# include <time.h> // date/time utilities
# include <ctype.h> // utilities to check chars
2016-01-17 14:54:54 +01:00
/* STM32 (including CM3) libraries */
2016-01-18 16:23:35 +01:00
# include <libopencmsis/core_cm3.h> // Cortex M3 utilities
2016-10-23 17:42:27 +02:00
# include <libopencm3/cm3/scb.h> // vector table definition
2016-01-29 11:25:30 +01:00
# include <libopencm3/cm3/nvic.h> // interrupt utilities
2016-10-23 17:42:27 +02:00
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/rcc.h> // real-time control clock library
2016-01-29 11:25:30 +01:00
# include <libopencm3/stm32/exti.h> // external interrupt utilities
2016-08-14 21:02:38 +02:00
# include <libopencm3/stm32/rtc.h> // real time clock utilities
2016-10-23 17:42:27 +02:00
# include <libopencm3/stm32/iwdg.h> // independent watchdog utilities
# include <libopencm3/stm32/dbgmcu.h> // debug utilities
2018-04-06 17:37:17 +02:00
# include <libopencm3/stm32/desig.h> // design utilities
2016-10-23 17:42:27 +02:00
# include <libopencm3/stm32/flash.h> // flash utilities
2020-03-06 11:10:46 +01:00
# include <libopencm3/stm32/f1/bkp.h> // backup domain utilities
2020-06-21 09:59:52 +02:00
# include <libopencm3/stm32/timer.h> // timer utilities
2016-01-17 14:54:54 +01:00
/* own libraries */
2016-01-28 21:21:50 +01:00
# include "global.h" // board definitions
2017-04-03 13:32:45 +02:00
# include "print.h" // printing utilities
2018-02-18 15:18:42 +01:00
# include "uart.h" // USART utilities
2016-01-18 16:23:35 +01:00
# include "usb_cdcacm.h" // USB CDC ACM utilities
2018-04-06 17:37:17 +02:00
# include "terminal.h" // handle the terminal interface
# include "menu.h" // menu utilities
2020-06-21 09:59:52 +02:00
# include "led_ws2812b.h" // WS2812B RGB LED control
2016-01-17 14:54:54 +01:00
2020-02-02 21:19:03 +01:00
/** watchdog period in ms */
# define WATCHDOG_PERIOD 10000
2019-06-18 17:43:43 +02:00
/** set to 0 if the RTC is reset when the board is powered on, only indicates the uptime
* set to 1 if VBAT can keep the RTC running when the board is unpowered , indicating the date and time
*/
# define RTC_DATE_TIME 1
2020-02-02 21:19:03 +01:00
/** number of RTC ticks per second
* @ note use integer divider of oscillator to keep second precision
*/
2020-03-06 11:10:46 +01:00
# define RTC_TICKS_SECOND 1
2019-06-18 17:43:43 +02:00
2020-03-05 14:04:51 +01:00
# if defined(RTC_DATE_TIME) && RTC_DATE_TIME
/** the start time from which to RTC ticks count
* @ note this allows the 32 - bit value to reach further in time , particularly when there are several ticks per second
*/
const time_t rtc_offset = 1577833200 ; // We 1. Jan 00:00:00 CET 2020
# endif
2019-06-18 17:43:43 +02:00
/** RTC time when device is started */
static time_t time_start = 0 ;
2016-10-23 17:42:27 +02:00
2016-08-14 21:02:38 +02:00
/** @defgroup main_flags flag set in interrupts to be processed in main task
* @ {
*/
volatile bool rtc_internal_tick_flag = false ; /**< flag set when internal RTC ticked */
/** @} */
2020-03-06 11:11:53 +01:00
/** GPIO pin connected to relay, used to control button connection to panel */
# define RELAY_PANEL_PIN PB6
/** GPIO pin connected to relay, used to simulate button press */
# define RELAY_BUTTON_PIN PB7
/** GPIO for button 1 */
2020-06-21 09:59:52 +02:00
# define BUTTON1_PIN PB9
2020-03-06 11:11:53 +01:00
/** GPIO for button 2 */
2020-06-21 09:59:52 +02:00
# define BUTTON2_PIN PB8
2020-03-06 11:11:53 +01:00
/** which button has been pressed */
volatile uint8_t button_pressed = 0 ;
/** if we apply the opening policy */
bool opening_apply = false ;
2020-06-21 09:59:52 +02:00
uint8_t pattern_length = 0 ;
2020-03-06 11:11:53 +01:00
static struct opening_settings_t {
uint8_t days ; /**< which days of the week it door access applies (bit 7 = Monday) */
uint16_t start_time ; /**< at which minutes of the day to start */
uint16_t stop_time ; /**< at which minutes of the day to stop */
uint8_t button_pattern [ 10 ] ; /**< sequence of buttons to press to open the door */
} opening_settings ;
2020-06-21 09:59:52 +02:00
/** timer to generate the ticks for the button LED animations */
# define LED_ANIMATION_TIMER 2
/** number of timer ticks passed, for the LED animation */
static volatile uint8_t led_animation_ticks = 0 ;
/** the button LED animation for the rust fade (duration in ticks, R, G, B) */
static const uint8_t rust_animation [ ] [ 4 ] = {
{ 0 , 0 , 0 , 0 } ,
{ 1 , 0xb7 / 10 * 1 , 0x41 / 10 * 1 , 0x0e / 10 * 1 } ,
{ 1 , 0xb7 / 10 * 2 , 0x41 / 10 * 2 , 0x0e / 10 * 2 } ,
{ 1 , 0xb7 / 10 * 3 , 0x41 / 10 * 3 , 0x0e / 10 * 3 } ,
{ 1 , 0xb7 / 10 * 4 , 0x41 / 10 * 4 , 0x0e / 10 * 4 } ,
{ 1 , 0xb7 / 10 * 5 , 0x41 / 10 * 5 , 0x0e / 10 * 5 } ,
{ 1 , 0xb7 / 10 * 4 , 0x41 / 10 * 4 , 0x0e / 10 * 4 } ,
{ 1 , 0xb7 / 10 * 3 , 0x41 / 10 * 3 , 0x0e / 10 * 3 } ,
{ 1 , 0xb7 / 10 * 2 , 0x41 / 10 * 2 , 0x0e / 10 * 2 } ,
{ 1 , 0xb7 / 10 * 1 , 0x41 / 10 * 1 , 0x0e / 10 * 1 } ,
{ 0 , 0 , 0 , 0 } ,
} ;
/** the button LED animation for the strobe (duration in ticks, R, G, B) */
static const uint8_t strobe_animation [ ] [ 4 ] = {
{ 0 , 0 , 0 , 0 } ,
{ 1 , 0xff / 2 , 0xff / 2 , 0xff / 2 } ,
{ 2 , 0 , 0 , 0 } ,
{ 1 , 0xff / 2 , 0xff / 2 , 0xff / 2 } ,
{ 0 , 0 , 0 , 0 } ,
} ;
2020-03-06 11:11:53 +01:00
/** save current opening_settings into SRAM */
static void save_opening_settings ( void )
{
BKP_DR1 = 0 ; // invalid saved settings
BKP_DR2 = opening_settings . days & 0x7f ;
BKP_DR3 = opening_settings . start_time ;
BKP_DR4 = opening_settings . stop_time ;
BKP_DR5 = opening_settings . button_pattern [ 0 ] ;
BKP_DR6 = opening_settings . button_pattern [ 1 ] ;
BKP_DR7 = opening_settings . button_pattern [ 2 ] ;
BKP_DR8 = opening_settings . button_pattern [ 3 ] ;
BKP_DR9 = opening_settings . button_pattern [ 4 ] ;
BKP_DR10 = opening_settings . button_pattern [ 5 ] ;
BKP_DR11 = opening_settings . button_pattern [ 6 ] ;
BKP_DR12 = opening_settings . button_pattern [ 7 ] ;
BKP_DR13 = opening_settings . button_pattern [ 8 ] ;
BKP_DR14 = opening_settings . button_pattern [ 9 ] ;
BKP_DR1 = 0x4223 ; //validate saved setting
}
2017-04-03 13:32:45 +02:00
size_t putc ( char c )
2016-01-17 14:54:54 +01:00
{
2017-04-03 13:32:45 +02:00
size_t length = 0 ; // number of characters printed
2018-04-06 17:37:17 +02:00
static char last_c = 0 ; // to remember on which character we last sent
if ( ' \n ' = = c ) { // send carriage return (CR) + line feed (LF) newline for each LF
if ( ' \r ' ! = last_c ) { // CR has not already been sent
2019-09-19 11:59:02 +02:00
# if !defined(STLINKV2)
2018-02-18 15:18:42 +01:00
uart_putchar_nonblocking ( ' \r ' ) ; // send CR over USART
2018-10-29 15:03:29 +01:00
# endif
2017-04-19 16:24:07 +02:00
usb_cdcacm_putchar ( ' \r ' ) ; // send CR over USB
2018-04-06 17:37:17 +02:00
length + + ; // remember we printed 1 character
2016-01-17 14:54:54 +01:00
}
}
2019-09-19 11:59:02 +02:00
# if !defined(STLINKV2)
2018-04-06 17:37:17 +02:00
uart_putchar_nonblocking ( c ) ; // send byte over USART
2018-10-29 15:03:29 +01:00
# endif
2018-04-06 17:37:17 +02:00
usb_cdcacm_putchar ( c ) ; // send byte over USB
length + + ; // remember we printed 1 character
last_c = c ; // remember last character
2019-03-26 19:27:40 +01:00
return length ; // return number of characters printed
2016-01-17 14:54:54 +01:00
}
2018-04-06 17:37:17 +02:00
/** display available commands
* @ param [ in ] argument no argument required
*/
static void command_help ( void * argument ) ;
/** show software and hardware version
* @ param [ in ] argument no argument required
*/
static void command_version ( void * argument ) ;
/** show uptime
* @ param [ in ] argument no argument required
*/
static void command_uptime ( void * argument ) ;
2019-06-18 17:43:43 +02:00
# if RTC_DATE_TIME
/** show date and time
* @ param [ in ] argument date and time to set
*/
static void command_datetime ( void * argument ) ;
# endif
2018-04-06 17:37:17 +02:00
/** reset board
* @ param [ in ] argument no argument required
*/
static void command_reset ( void * argument ) ;
/** switch to DFU bootloader
* @ param [ in ] argument no argument required
*/
2020-03-05 14:10:15 +01:00
static void command_bootloader_dfu ( void * argument ) ;
/** switch to system memory / embedded USART bootloader
* @ param [ in ] argument no argument required
*/
static void command_bootloader_embedded ( void * argument ) ;
2018-04-06 17:37:17 +02:00
2020-03-06 11:11:53 +01:00
/** show/set on which days the access policy applies
* @ param [ in ] argument 7 x0 / 1 to enable day of the week , starting with Monday ( optional )
*/
static void command_days ( void * argument )
{
const char * days = ( char * ) argument ; // argument is optional days
if ( NULL ! = argument ) { // days are provided, parse and save them
bool valid = ( 7 = = strlen ( days ) ) ; // verify input string
for ( uint8_t day = 0 ; day < 7 & & valid ; day + + ) {
if ( days [ day ] ! = ' 0 ' & & days [ day ] ! = ' 1 ' ) {
valid = false ;
}
}
if ( valid ) { // save provided settings
// parse new days
opening_settings . days = 0 ;
for ( uint8_t day = 0 ; day < 7 ; day + + ) {
if ( ' 1 ' = = days [ day ] ) {
opening_settings . days | = ( 1 < < ( 6 - day ) ) ;
}
}
save_opening_settings ( ) ; // save days
puts ( " days saved \n " ) ;
} else {
puts ( " provide exactly 7 times 0 (off) or 1 (on). 1st digit for Monday, 7th digit for Sunday \n " ) ;
}
}
// display current days
printf ( " opening days: %07b \n " , opening_settings . days ) ;
const char * day_names [ ] = { " Monday " , " Tuesday " , " Wednesday " , " Thursday " , " Friday " , " Saturday " , " Sunday " } ;
for ( uint8_t day = 0 ; day < LENGTH ( day_names ) ; day + + ) {
printf ( " - %s: %s \n " , day_names [ day ] , ( opening_settings . days & ( 1 < < ( 6 - day ) ) ) ? " on " : " off " ) ;
}
}
/** show/set on which time the access policy starts applying
* @ param [ in ] argument string with time of day , optional
*/
static void command_start ( void * argument )
{
const char * time = ( char * ) argument ; // argument is optional time
if ( NULL ! = argument ) { // days are provided, parse and save them
bool valid = ( 5 = = strlen ( time ) ) ; // verify input string
if ( ! ( valid & & isdigit ( ( int8_t ) time [ 0 ] ) & & isdigit ( ( int8_t ) time [ 1 ] ) & & ' : ' = = time [ 2 ] & & isdigit ( ( int8_t ) time [ 3 ] ) & & isdigit ( ( int8_t ) time [ 4 ] ) ) ) {
valid = false ;
}
if ( valid ) { // save provided settings
opening_settings . start_time = 0 ;
opening_settings . start_time + = ( time [ 4 ] - ' 0 ' ) * 1 ;
opening_settings . start_time + = ( time [ 3 ] - ' 0 ' ) * 10 ;
opening_settings . start_time + = ( time [ 1 ] - ' 0 ' ) * 60 ;
opening_settings . start_time + = ( time [ 0 ] - ' 0 ' ) * 600 ;
save_opening_settings ( ) ; // save days
puts ( " start time saved \n " ) ;
} else {
puts ( " provide time in HH:MM format \n " ) ;
}
}
printf ( " start time: %02u:%02u \n " , opening_settings . start_time / 60 , opening_settings . start_time % 60 ) ;
}
/** show/set on which time the access policy stops applying
* @ param [ in ] argument string with time of day , optional
*/
static void command_stop ( void * argument )
{
const char * time = ( char * ) argument ; // argument is optional time
if ( NULL ! = argument ) { // days are provided, parse and save them
bool valid = ( 5 = = strlen ( time ) ) ; // verify input string
if ( ! ( valid & & isdigit ( ( int8_t ) time [ 0 ] ) & & isdigit ( ( int8_t ) time [ 1 ] ) & & ' : ' = = time [ 2 ] & & isdigit ( ( int8_t ) time [ 3 ] ) & & isdigit ( ( int8_t ) time [ 4 ] ) ) ) {
valid = false ;
}
if ( valid ) { // save provided settings
opening_settings . stop_time = 0 ;
opening_settings . stop_time + = ( time [ 4 ] - ' 0 ' ) * 1 ;
opening_settings . stop_time + = ( time [ 3 ] - ' 0 ' ) * 10 ;
opening_settings . stop_time + = ( time [ 1 ] - ' 0 ' ) * 60 ;
opening_settings . stop_time + = ( time [ 0 ] - ' 0 ' ) * 600 ;
save_opening_settings ( ) ; // save days
puts ( " stop time saved \n " ) ;
} else {
puts ( " provide time in HH:MM format \n " ) ;
}
}
printf ( " stop time: %02u:%02u \n " , opening_settings . stop_time / 60 , opening_settings . stop_time % 60 ) ;
}
/** open door by simulating button press
* @ param [ in ] argument not used
*/
static void command_open ( void * argument )
{
( void ) argument ; // we won't use the argument
2020-06-21 09:59:52 +02:00
gpio_set ( GPIO_PORT ( RELAY_PANEL_PIN ) , GPIO_PIN ( RELAY_PANEL_PIN ) ) ; // set high to activate relay and take control over the button
gpio_set ( GPIO_PORT ( RELAY_BUTTON_PIN ) , GPIO_PIN ( RELAY_BUTTON_PIN ) ) ; // set high to activate relay an simulate button press
2020-03-06 11:11:53 +01:00
sleep_ms ( 1000 ) ; // hold button a bit
2020-06-21 09:59:52 +02:00
gpio_clear ( GPIO_PORT ( RELAY_BUTTON_PIN ) , GPIO_PIN ( RELAY_BUTTON_PIN ) ) ; // set low to deactivate relay and release button
2020-03-06 11:11:53 +01:00
if ( ! opening_apply ) {
2020-06-21 09:59:52 +02:00
gpio_clear ( GPIO_PORT ( RELAY_PANEL_PIN ) , GPIO_PIN ( RELAY_PANEL_PIN ) ) ; // set low to deactivate relay and git control back to button
2020-03-06 11:11:53 +01:00
}
}
/** show/set button pattern
* @ param [ in ] argument sequence of 1 / 2
*/
static void command_pattern ( void * argument )
{
const char * pattern = ( char * ) argument ; // argument is optional pattern
if ( NULL ! = argument ) { // pattern provided
bool valid = ( LENGTH ( opening_settings . button_pattern ) > = strlen ( pattern ) ) ; // verify input string
for ( uint8_t i = 0 ; i < strlen ( pattern ) & & valid ; i + + ) {
if ( ' 1 ' ! = pattern [ i ] & & ' 2 ' ! = pattern [ i ] ) {
valid = false ;
}
}
if ( valid ) { // save provided settings
// reset pattern
for ( uint8_t i = 0 ; i < LENGTH ( opening_settings . button_pattern ) ; i + + ) {
opening_settings . button_pattern [ i ] = 0 ;
}
// save new pattern
for ( uint8_t i = 0 ; i < strlen ( pattern ) ; i + + ) {
opening_settings . button_pattern [ i ] = pattern [ i ] - ' 0 ' ;
}
save_opening_settings ( ) ; // save days
puts ( " button sequence saved \n " ) ;
} else {
printf ( " provide buttons sequence of up to %u 1 or 2 \n " , LENGTH ( opening_settings . button_pattern ) ) ;
}
2020-06-21 09:59:52 +02:00
for ( pattern_length = 0 ; pattern_length < LENGTH ( opening_settings . button_pattern ) & & opening_settings . button_pattern [ pattern_length ] ; pattern_length + + ) ;
2020-03-06 11:11:53 +01:00
}
if ( 0 = = opening_settings . button_pattern [ 0 ] ) {
puts ( " no button sequence set \n " ) ;
} else {
puts ( " button sequence: " ) ;
for ( uint8_t i = 0 ; i < LENGTH ( opening_settings . button_pattern ) & & opening_settings . button_pattern [ i ] ; i + + ) {
putc ( opening_settings . button_pattern [ i ] + ' 0 ' ) ;
}
putc ( ' \n ' ) ;
}
}
2020-06-21 09:59:52 +02:00
/** test LEDs
* @ param [ in ] argument " on " or " off "
*/
static void command_led ( void * argument )
{
const char * onoff = ( char * ) argument ; // if it should be switched on or off
if ( NULL = = onoff | | 0 = = strlen ( onoff ) ) {
puts ( " say if the LEDs should be switched on or off \n " ) ;
} else if ( 0 = = strcmp ( onoff , " on " ) ) {
for ( uint8_t led = 0 ; led < LED_WS2812B_LEDS ; led + + ) {
led_ws2812b_set_rgb ( led , 0x20 , 0x20 , 0x20 ) ;
}
} else if ( 0 = = strcmp ( onoff , " off " ) ) {
for ( uint8_t led = 0 ; led < LED_WS2812B_LEDS ; led + + ) {
led_ws2812b_set_rgb ( led , 0 , 0 , 0 ) ;
}
} else {
printf ( " unknown argument %s \n " , onoff ) ;
}
}
2018-04-06 17:37:17 +02:00
/** list of all supported commands */
static const struct menu_command_t menu_commands [ ] = {
{
. shortcut = ' h ' ,
. name = " help " ,
. command_description = " display help " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_help ,
} ,
{
. shortcut = ' v ' ,
. name = " version " ,
. command_description = " show software and hardware version " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_version ,
} ,
{
. shortcut = ' u ' ,
. name = " uptime " ,
. command_description = " show uptime " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_uptime ,
} ,
2019-06-18 17:43:43 +02:00
# if RTC_DATE_TIME
{
2020-03-06 11:11:53 +01:00
. shortcut = ' D ' ,
2019-06-18 17:43:43 +02:00
. name = " date " ,
2019-07-29 18:33:05 +02:00
. command_description = " show/set date and time " ,
2019-06-18 17:43:43 +02:00
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [YYYY-MM-DD HH:MM:SS] " ,
. command_handler = & command_datetime ,
} ,
# endif
2018-04-06 17:37:17 +02:00
{
. shortcut = ' r ' ,
. name = " reset " ,
. command_description = " reset board " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_reset ,
} ,
{
. shortcut = ' b ' ,
. name = " bootloader " ,
. command_description = " reboot into DFU bootloader " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
2020-03-05 14:10:15 +01:00
. command_handler = & command_bootloader_dfu ,
} ,
{
. shortcut = ' B ' ,
. name = " embedded " ,
. command_description = " boot embedded USART bootloader " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_bootloader_embedded ,
2018-04-06 17:37:17 +02:00
} ,
2020-03-06 11:11:53 +01:00
{
. shortcut = ' d ' ,
. name = " days " ,
. command_description = " on which days to apply the access policy " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [0001000, 0/1 for Monday to Sunday] " ,
. command_handler = & command_days ,
} ,
{
. shortcut = ' s ' ,
. name = " start " ,
. command_description = " on which time to start the access policy " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [HH:MM] " ,
. command_handler = & command_start ,
} ,
{
. shortcut = ' S ' ,
. name = " stop " ,
. command_description = " on which time to stop the access policy " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [HH:MM] " ,
. command_handler = & command_stop ,
} ,
{
. shortcut = ' o ' ,
. name = " open " ,
. command_description = " open door " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_open ,
} ,
2020-06-21 09:59:52 +02:00
{
. shortcut = ' l ' ,
. name = " led " ,
. command_description = " test LEDs " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " on|off " ,
. command_handler = & command_led ,
} ,
2020-03-06 11:11:53 +01:00
{
. shortcut = ' p ' ,
. name = " password " ,
. command_description = " set/show password button sequence " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [sequence of 1/2] " ,
. command_handler = & command_pattern ,
} ,
2018-04-06 17:37:17 +02:00
} ;
static void command_help ( void * argument )
{
( void ) argument ; // we won't use the argument
printf ( " available commands: \n " ) ;
menu_print_commands ( menu_commands , LENGTH ( menu_commands ) ) ; // print global commands
}
static void command_version ( void * argument )
{
( void ) argument ; // we won't use the argument
printf ( " firmware date: %04u-%02u-%02u \n " , BUILD_YEAR , BUILD_MONTH , BUILD_DAY ) ; // show firmware build date
2020-03-16 20:55:50 +01:00
const uint16_t dev_id = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK ;
2020-03-15 14:11:55 +01:00
const uint16_t rev_id = DBGMCU_IDCODE > > 16 ;
2020-06-21 09:59:25 +02:00
printf ( " chip: ID=0x%03x, rev=0x%04x \n " , dev_id , rev_id ) ;
2018-04-06 17:37:17 +02:00
// show flash size
2020-02-19 21:16:07 +01:00
puts ( " flash size: " ) ;
2019-06-18 17:44:50 +02:00
if ( 0xffff = = DESIG_FLASH_SIZE ) {
2020-02-19 21:16:07 +01:00
puts ( " unknown (probably a defective micro-controller \n " ) ;
2018-04-06 17:37:17 +02:00
} else {
printf ( " %u KB \n " , DESIG_FLASH_SIZE ) ;
}
// display device identity
2020-03-15 14:11:55 +01:00
printf ( " device id: %08x%08x%04x%04x \n " , DESIG_UNIQUE_ID2 , DESIG_UNIQUE_ID1 , DESIG_UNIQUE_ID0 & 0xffff , DESIG_UNIQUE_ID0 > > 16 ) ;
2018-04-06 17:37:17 +02:00
}
static void command_uptime ( void * argument )
{
( void ) argument ; // we won't use the argument
2020-02-17 18:08:17 +01:00
uint32_t uptime = ( rtc_get_counter_val ( ) - time_start ) / RTC_TICKS_SECOND ; // get time from internal RTC
2019-06-18 17:43:43 +02:00
printf ( " uptime: %u.%02u:%02u:%02u \n " , uptime / ( 24 * 60 * 60 ) , ( uptime / ( 60 * 60 ) ) % 24 , ( uptime / 60 ) % 60 , uptime % 60 ) ;
2018-04-06 17:37:17 +02:00
}
2019-06-18 17:43:43 +02:00
# if RTC_DATE_TIME
static void command_datetime ( void * argument )
{
char * datetime = ( char * ) argument ; // argument is optional date time
if ( NULL = = argument ) { // no date and time provided, just show the current day and time
2020-03-05 14:04:51 +01:00
const time_t time_rtc = rtc_get_counter_val ( ) / RTC_TICKS_SECOND + rtc_offset ; // get time from internal RTC
const struct tm * time_tm = localtime ( & time_rtc ) ; // convert time
const char * days [ ] = { " Su " , " Mo " , " Tu " , " We " , " Th " , " Fr " , " Sa " } ; // the days of the week
printf ( " date: %s %d-%02d-%02d %02d:%02d:%02d \n " , days [ time_tm - > tm_wday ] , 1900 + time_tm - > tm_year , 1 + time_tm - > tm_mon , time_tm - > tm_mday , time_tm - > tm_hour , time_tm - > tm_min , time_tm - > tm_sec ) ;
2019-06-18 17:43:43 +02:00
} else { // date and time provided, set it
const char * malformed = " date and time malformed, expecting YYYY-MM-DD HH:MM:SS \n " ;
struct tm time_tm ; // to store the parsed date time
if ( strlen ( datetime ) ! = ( 4 + 1 + 2 + 1 + 2 ) + 1 + ( 2 + 1 + 2 + 1 + 2 ) ) { // verify date/time is long enough
printf ( malformed ) ;
return ;
}
if ( ! ( isdigit ( ( int8_t ) datetime [ 0 ] ) & & isdigit ( ( int8_t ) datetime [ 1 ] ) & & isdigit ( ( int8_t ) datetime [ 2 ] ) & & isdigit ( ( int8_t ) datetime [ 3 ] ) & & ' - ' = = datetime [ 4 ] & & isdigit ( ( int8_t ) datetime [ 5 ] ) & & isdigit ( ( int8_t ) datetime [ 6 ] ) & & ' - ' = = datetime [ 7 ] & & isdigit ( ( int8_t ) datetime [ 8 ] ) & & isdigit ( ( int8_t ) datetime [ 9 ] ) & & ' ' = = datetime [ 10 ] & & isdigit ( ( int8_t ) datetime [ 11 ] ) & & isdigit ( ( int8_t ) datetime [ 12 ] ) & & ' : ' = = datetime [ 13 ] & & isdigit ( ( int8_t ) datetime [ 14 ] ) & & isdigit ( ( int8_t ) datetime [ 15 ] ) & & ' : ' = = datetime [ 16 ] & & isdigit ( ( int8_t ) datetime [ 17 ] ) & & isdigit ( ( int8_t ) datetime [ 18 ] ) ) ) { // verify format (good enough to not fail parsing)
printf ( malformed ) ;
return ;
}
time_tm . tm_year = strtol ( & datetime [ 0 ] , NULL , 10 ) - 1900 ; // parse year
2020-03-05 14:04:51 +01:00
time_tm . tm_mon = strtol ( & datetime [ 5 ] , NULL , 10 ) - 1 ; // parse month
2019-06-18 17:43:43 +02:00
time_tm . tm_mday = strtol ( & datetime [ 8 ] , NULL , 10 ) ; // parse day
time_tm . tm_hour = strtol ( & datetime [ 11 ] , NULL , 10 ) ; // parse hour
time_tm . tm_min = strtol ( & datetime [ 14 ] , NULL , 10 ) ; // parse minutes
time_tm . tm_sec = strtol ( & datetime [ 17 ] , NULL , 10 ) ; // parse seconds
time_t time_rtc = mktime ( & time_tm ) ; // get back seconds
2020-03-05 14:04:51 +01:00
time_rtc - = rtc_offset ; // remove start offset
2020-02-17 18:08:17 +01:00
time_start = time_rtc * RTC_TICKS_SECOND + ( rtc_get_counter_val ( ) - time_start ) ; // update uptime with current date
rtc_set_counter_val ( time_rtc * RTC_TICKS_SECOND ) ; // save date/time to internal RTC
2020-03-05 14:04:51 +01:00
printf ( " date and time saved: %d-%02d-%02d %02d:%02d:%02d \n " , 1900 + time_tm . tm_year , 1 + time_tm . tm_mon , time_tm . tm_mday , time_tm . tm_hour , time_tm . tm_min , time_tm . tm_sec ) ;
2019-06-18 17:43:43 +02:00
}
}
# endif
2018-04-06 17:37:17 +02:00
static void command_reset ( void * argument )
{
( void ) argument ; // we won't use the argument
scb_reset_system ( ) ; // reset device
while ( true ) ; // wait for the reset to happen
}
2020-03-05 14:10:15 +01:00
static void command_bootloader_dfu ( void * argument )
2018-04-06 17:37:17 +02:00
{
( void ) argument ; // we won't use the argument
2020-03-06 11:11:53 +01:00
// disable relays
gpio_clear ( GPIO_PORT ( RELAY_PANEL_PIN ) , GPIO_PIN ( RELAY_PANEL_PIN ) ) ;
gpio_clear ( GPIO_PORT ( RELAY_BUTTON_PIN ) , GPIO_PIN ( RELAY_BUTTON_PIN ) ) ;
2020-01-02 13:01:40 +01:00
// set DFU magic to specific RAM location
__dfu_magic [ 0 ] = ' D ' ;
__dfu_magic [ 1 ] = ' F ' ;
__dfu_magic [ 2 ] = ' U ' ;
__dfu_magic [ 3 ] = ' ! ' ;
scb_reset_system ( ) ; // reset system (core and peripherals)
2018-04-06 17:37:17 +02:00
while ( true ) ; // wait for the reset to happen
}
2016-01-17 14:54:54 +01:00
2020-03-05 14:10:15 +01:00
static void command_bootloader_embedded ( void * argument )
{
( void ) argument ; // we won't use the argument
2020-03-06 11:11:53 +01:00
// disable relays
gpio_clear ( GPIO_PORT ( RELAY_PANEL_PIN ) , GPIO_PIN ( RELAY_PANEL_PIN ) ) ;
gpio_clear ( GPIO_PORT ( RELAY_BUTTON_PIN ) , GPIO_PIN ( RELAY_BUTTON_PIN ) ) ;
2020-03-05 14:10:15 +01:00
// set watchdog to exit system memory after some time
iwdg_set_period_ms ( 25000 ) ; // set independent watchdog period (26214.4 ms if the max timeout)
iwdg_start ( ) ; // start independent watchdog
iwdg_reset ( ) ; // restart timer
// start system memory
const uint32_t address = 0x1FFFF000 ; // system memory address
SCB_VTOR = ( volatile uint32_t ) ( address ) ; // set vector table to application vector table (store at the beginning of the application)
__asm__ volatile ( " MSR msp,%0 " : : " r " ( * ( uint32_t * ) address ) ) ; // set stack pointer to address provided in the beginning of the application (loaded into a register first)
( * ( void ( * * ) ( void ) ) ( ( uint32_t ) address + 4 ) ) ( ) ; // start system memory (by jumping to the reset function which address is stored as second entry of the vector table)
while ( true ) ; // this should not be reached
}
2016-08-14 21:02:38 +02:00
/** process user command
* @ param [ in ] str user command string ( \ 0 ended )
*/
static void process_command ( char * str )
2016-01-17 14:54:54 +01:00
{
2018-04-06 17:37:17 +02:00
// ensure actions are available
2019-06-18 17:44:50 +02:00
if ( NULL = = menu_commands | | 0 = = LENGTH ( menu_commands ) ) {
2018-04-06 17:37:17 +02:00
return ;
2016-08-14 21:02:38 +02:00
}
2018-04-06 17:37:17 +02:00
// don't handle empty lines
2019-06-18 17:44:50 +02:00
if ( ! str | | 0 = = strlen ( str ) ) {
2018-04-06 17:37:17 +02:00
return ;
}
bool command_handled = false ;
if ( ! command_handled ) {
command_handled = menu_handle_command ( str , menu_commands , LENGTH ( menu_commands ) ) ; // try if this is not a global command
}
if ( ! command_handled ) {
printf ( " command not recognized. enter help to list commands \n " ) ;
2016-08-14 21:02:38 +02:00
}
2016-01-17 14:54:54 +01:00
}
2016-08-14 21:02:38 +02:00
/** program entry point
* this is the firmware function started by the micro - controller
*/
2016-10-23 17:42:27 +02:00
void main ( void ) ;
void main ( void )
2018-04-06 17:37:17 +02:00
{
2016-01-29 00:24:49 +01:00
rcc_clock_setup_in_hse_8mhz_out_72mhz ( ) ; // use 8 MHz high speed external clock to generate 72 MHz internal clock
2016-08-14 21:02:38 +02:00
2016-10-23 17:42:27 +02:00
# if DEBUG
2017-01-30 09:44:51 +01:00
// enable functionalities for easier debug
2016-10-23 17:42:27 +02:00
DBGMCU_CR | = DBGMCU_CR_IWDG_STOP ; // stop independent watchdog counter when code is halted
DBGMCU_CR | = DBGMCU_CR_WWDG_STOP ; // stop window watchdog counter when code is halted
DBGMCU_CR | = DBGMCU_CR_STANDBY ; // allow debug also in standby mode (keep digital part and clock powered)
DBGMCU_CR | = DBGMCU_CR_STOP ; // allow debug also in stop mode (keep clock powered)
DBGMCU_CR | = DBGMCU_CR_SLEEP ; // allow debug also in sleep mode (keep clock powered)
2017-02-06 17:40:28 +01:00
# else
2017-01-30 09:44:51 +01:00
// setup watchdog to reset in case we get stuck (i.e. when an error occurred)
iwdg_set_period_ms ( WATCHDOG_PERIOD ) ; // set independent watchdog period
iwdg_start ( ) ; // start independent watchdog
2016-10-23 17:42:27 +02:00
# endif
2017-04-03 13:32:45 +02:00
board_setup ( ) ; // setup board
2019-09-19 11:59:02 +02:00
# if !defined(STLINKV2)
2018-02-18 15:18:42 +01:00
uart_setup ( ) ; // setup USART (for printing)
2018-10-29 15:03:29 +01:00
# endif
2017-04-19 16:26:40 +02:00
usb_cdcacm_setup ( ) ; // setup USB CDC ACM (for printing)
2020-03-06 11:11:53 +01:00
puts ( " \n welcome to the dachboden door panel \n " ) ; // print welcome message
2016-01-29 00:24:49 +01:00
2019-12-21 19:26:27 +01:00
# if DEBUG
// show reset cause
if ( RCC_CSR & ( RCC_CSR_LPWRRSTF | RCC_CSR_WWDGRSTF | RCC_CSR_IWDGRSTF | RCC_CSR_SFTRSTF | RCC_CSR_PORRSTF | RCC_CSR_PINRSTF ) ) {
puts ( " reset cause(s): " ) ;
if ( RCC_CSR & RCC_CSR_LPWRRSTF ) {
puts ( " low-power " ) ;
}
if ( RCC_CSR & RCC_CSR_WWDGRSTF ) {
puts ( " window-watchdog " ) ;
}
if ( RCC_CSR & RCC_CSR_IWDGRSTF ) {
puts ( " independent-watchdog " ) ;
}
if ( RCC_CSR & RCC_CSR_SFTRSTF ) {
puts ( " software " ) ;
}
if ( RCC_CSR & RCC_CSR_PORRSTF ) {
puts ( " POR/PDR " ) ;
}
if ( RCC_CSR & RCC_CSR_PINRSTF ) {
puts ( " pin " ) ;
}
putc ( ' \n ' ) ;
RCC_CSR | = RCC_CSR_RMVF ; // clear reset flags
}
# endif
2017-01-30 09:44:51 +01:00
# if !(DEBUG)
// show watchdog information
2019-12-21 19:26:58 +01:00
printf ( " setup watchdog: %.2fs " , WATCHDOG_PERIOD / 1000.0 ) ;
if ( FLASH_OBR & FLASH_OBR_OPTERR ) {
2020-02-19 21:16:07 +01:00
puts ( " (option bytes not set in flash: software wachtdog used, not automatically started at reset) \n " ) ;
2019-12-21 19:26:58 +01:00
} else if ( FLASH_OBR & FLASH_OBR_WDG_SW ) {
2020-02-19 21:16:07 +01:00
puts ( " (software watchdog used, not automatically started at reset) \n " ) ;
2017-01-30 09:44:51 +01:00
} else {
2020-02-19 21:16:07 +01:00
puts ( " (hardware watchdog used, automatically started at reset) \n " ) ;
2017-01-30 09:44:51 +01:00
}
# endif
2016-08-14 21:02:38 +02:00
// setup RTC
2020-02-19 21:16:07 +01:00
puts ( " setup internal RTC: " ) ;
2020-03-06 11:10:46 +01:00
// note: the blue pill LSE oscillator is affected when toggling the onboard LED -> DON'T USE THE ONBOARD LED since we want to use the LSE
2020-02-02 21:19:03 +01:00
rtc_auto_awake ( RCC_LSE , 32768 / RTC_TICKS_SECOND - 1 ) ; // ensure internal RTC is on, uses the 32.678 kHz LSE, and the prescale is set to our tick speed, else update backup registers accordingly (power off the micro-controller for the change to take effect)
2016-08-14 21:02:38 +02:00
rtc_interrupt_enable ( RTC_SEC ) ; // enable RTC interrupt on "seconds"
nvic_enable_irq ( NVIC_RTC_IRQ ) ; // allow the RTC to interrupt
2019-06-18 17:43:43 +02:00
time_start = rtc_get_counter_val ( ) ; // get start time from internal RTC
2020-02-19 21:16:07 +01:00
puts ( " OK \n " ) ;
2016-08-14 21:02:38 +02:00
2020-03-06 11:11:53 +01:00
// setup relays
puts ( " setup relays: " ) ;
rcc_periph_clock_enable ( GPIO_RCC ( RELAY_PANEL_PIN ) ) ; // enable clock for GPIO domain
gpio_clear ( GPIO_PORT ( RELAY_PANEL_PIN ) , GPIO_PIN ( RELAY_PANEL_PIN ) ) ; // set low to leave per default
gpio_set_mode ( GPIO_PORT ( RELAY_PANEL_PIN ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO_PIN ( RELAY_PANEL_PIN ) ) ; // set as output to control the transistor controlling the relay
rcc_periph_clock_enable ( GPIO_RCC ( RELAY_BUTTON_PIN ) ) ; // enable clock for GPIO domain
gpio_clear ( GPIO_PORT ( RELAY_BUTTON_PIN ) , GPIO_PIN ( RELAY_BUTTON_PIN ) ) ; // set low to leave per default
gpio_set_mode ( GPIO_PORT ( RELAY_BUTTON_PIN ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO_PIN ( RELAY_BUTTON_PIN ) ) ; // set as output to control the transistor controlling the relay
puts ( " OK \n " ) ;
// setup buttons
puts ( " setup buttons: " ) ;
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable alternate function clock for external interrupt
rcc_periph_clock_enable ( GPIO_RCC ( BUTTON1_PIN ) ) ; // enable clock for button
gpio_set ( GPIO_PORT ( BUTTON1_PIN ) , GPIO_PIN ( BUTTON1_PIN ) ) ; // pull up to be able to detect button push (go low)
gpio_set_mode ( GPIO_PORT ( BUTTON1_PIN ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , GPIO_PIN ( BUTTON1_PIN ) ) ; // set button pin to input
exti_select_source ( GPIO_EXTI ( BUTTON1_PIN ) , GPIO_PORT ( BUTTON1_PIN ) ) ; // mask external interrupt of this pin only for this port
2020-06-21 09:59:52 +02:00
exti_set_trigger ( GPIO_EXTI ( BUTTON1_PIN ) , EXTI_TRIGGER_FALLING ) ; // trigger when button is pressed
2020-03-06 11:11:53 +01:00
exti_enable_request ( GPIO_EXTI ( BUTTON1_PIN ) ) ; // enable external interrupt
nvic_enable_irq ( GPIO_NVIC_EXTI_IRQ ( BUTTON1_PIN ) ) ; // enable interrupt
rcc_periph_clock_enable ( GPIO_RCC ( BUTTON2_PIN ) ) ; // enable clock for button
gpio_set ( GPIO_PORT ( BUTTON2_PIN ) , GPIO_PIN ( BUTTON2_PIN ) ) ; // pull up to be able to detect button push (go low)
gpio_set_mode ( GPIO_PORT ( BUTTON2_PIN ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , GPIO_PIN ( BUTTON2_PIN ) ) ; // set button pin to input
exti_select_source ( GPIO_EXTI ( BUTTON2_PIN ) , GPIO_PORT ( BUTTON2_PIN ) ) ; // mask external interrupt of this pin only for this port
2020-06-21 09:59:52 +02:00
exti_set_trigger ( GPIO_EXTI ( BUTTON2_PIN ) , EXTI_TRIGGER_FALLING ) ; // trigger when button is pressed
2020-03-06 11:11:53 +01:00
exti_enable_request ( GPIO_EXTI ( BUTTON2_PIN ) ) ; // enable external interrupt
nvic_enable_irq ( GPIO_NVIC_EXTI_IRQ ( BUTTON2_PIN ) ) ; // enable interrupt
puts ( " OK \n " ) ;
// read opening settings from SRAM
puts ( " reading access settings: " ) ;
RCC_APB1ENR | = ( RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN ) ; // enable power
PWR_CR | = PWR_CR_DBP ; // enable access
if ( 0x4223 = = BKP_DR1 ) { // the magic header is present
opening_settings . days = BKP_DR2 & 0x7f ;
opening_settings . start_time = BKP_DR3 ;
opening_settings . stop_time = BKP_DR4 ;
opening_settings . button_pattern [ 0 ] = BKP_DR5 ;
opening_settings . button_pattern [ 1 ] = BKP_DR6 ;
opening_settings . button_pattern [ 2 ] = BKP_DR7 ;
opening_settings . button_pattern [ 3 ] = BKP_DR8 ;
opening_settings . button_pattern [ 4 ] = BKP_DR9 ;
opening_settings . button_pattern [ 5 ] = BKP_DR10 ;
opening_settings . button_pattern [ 6 ] = BKP_DR11 ;
opening_settings . button_pattern [ 7 ] = BKP_DR12 ;
opening_settings . button_pattern [ 8 ] = BKP_DR13 ;
opening_settings . button_pattern [ 9 ] = BKP_DR14 ;
puts ( " loaded \n " ) ;
} else { // there are no settings saved
memset ( & opening_settings , 0 , sizeof ( struct opening_settings_t ) ) ; // clear all values
puts ( " default \n " ) ;
}
// figure out how many button need to be pressed
for ( pattern_length = 0 ; pattern_length < LENGTH ( opening_settings . button_pattern ) & & opening_settings . button_pattern [ pattern_length ] ; pattern_length + + ) ;
2020-06-21 09:59:52 +02:00
puts ( " setup bell LEDs: " ) ;
uint8_t animation_progress = 0 ; // index of the current animation
led_ws2812b_setup ( ) ;
for ( uint8_t led = 0 ; led < LED_WS2812B_LEDS ; led + + ) {
led_ws2812b_set_rgb ( led , 0x10 , 0x10 , 0x10 ) ;
}
puts ( " OK \n " ) ;
puts ( " setup animation timer: " ) ;
// setup timer to wait for minimal time before next transmission (after previous transmission or reception)
rcc_periph_clock_enable ( RCC_TIM ( LED_ANIMATION_TIMER ) ) ; // enable clock for timer block
rcc_periph_reset_pulse ( RST_TIM ( LED_ANIMATION_TIMER ) ) ; // reset timer state
timer_set_mode ( TIM ( LED_ANIMATION_TIMER ) , TIM_CR1_CKD_CK_INT , TIM_CR1_CMS_EDGE , TIM_CR1_DIR_UP ) ; // set timer mode, use undivided timer clock,edge alignment (simple count), and count up
timer_set_prescaler ( TIM ( LED_ANIMATION_TIMER ) , 1099 - 1 ) ; // set the prescaler so this 16 bits timer allows to wait for maximum 1s ( 1 / (72E6 / 1099 / (2**16)) = 1.0003s)
timer_set_period ( TIM ( LED_ANIMATION_TIMER ) , 0xffff / 16 ) ; // the timing is not defined in the specification. I tested until the communication was reliable (all requests get an response)
timer_clear_flag ( TIM ( LED_ANIMATION_TIMER ) , TIM_SR_UIF ) ; // clear flag
timer_enable_irq ( TIM ( LED_ANIMATION_TIMER ) , TIM_DIER_UIE ) ; // enable update interrupt for timer
nvic_enable_irq ( NVIC_TIM_IRQ ( LED_ANIMATION_TIMER ) ) ; // catch interrupt in service routine
puts ( " OK \n " ) ;
2018-04-06 17:37:17 +02:00
// setup terminal
terminal_prefix = " " ; // set default prefix
terminal_process = & process_command ; // set central function to process commands
terminal_setup ( ) ; // start terminal
2016-10-23 17:42:27 +02:00
2018-04-06 17:37:17 +02:00
// start main loop
2016-02-18 10:39:08 +01:00
bool action = false ; // if an action has been performed don't go to sleep
button_flag = false ; // reset button flag
2020-03-06 11:11:53 +01:00
uint32_t last_button_action = 0 ; // the last time a button has been pressed
uint8_t button_pattern [ LENGTH ( opening_settings . button_pattern ) ] ; // to store the input button pattern
uint8_t button_input = 0 ; // how many buttons have been pressed
2020-06-21 09:59:52 +02:00
bool rust_animated = false ; // if the rust animation started
bool strobe_animated = false ; // if the strobe animation started
2016-01-29 00:24:49 +01:00
while ( true ) { // infinite loop
2016-10-23 17:42:27 +02:00
iwdg_reset ( ) ; // kick the dog
2019-06-18 17:45:53 +02:00
if ( user_input_available ) { // user input is available
2016-02-18 10:39:08 +01:00
action = true ; // action has been performed
2016-01-29 00:24:49 +01:00
led_toggle ( ) ; // toggle LED
2018-04-06 17:37:17 +02:00
char c = user_input_get ( ) ; // store receive character
terminal_send ( c ) ; // send received character to terminal
2016-08-14 21:02:38 +02:00
}
2020-03-06 11:11:53 +01:00
if ( button_flag | | button_pressed ) { // user pressed button
2016-08-14 21:02:38 +02:00
action = true ; // action has been performed
2020-06-21 09:59:52 +02:00
sleep_ms ( 200 ) ; // wait a bit to remove noise and double trigger
if ( ! gpio_get ( GPIO_PORT ( BUTTON1_PIN ) , GPIO_PIN ( BUTTON1_PIN ) ) ) {
button_pressed = 1 ;
}
if ( ! gpio_get ( GPIO_PORT ( BUTTON2_PIN ) , GPIO_PIN ( BUTTON2_PIN ) ) ) {
button_pressed = 2 ;
}
if ( button_pressed ) {
printf ( " button pressed: %u \n " , button_pressed ) ;
led_toggle ( ) ; // toggle LED
if ( pattern_length > 0 & & opening_apply ) { // only check pattern if there is one to compare to
// switch off LEDs
for ( uint8_t led = 0 ; led < LED_WS2812B_LEDS ; led + + ) {
led_ws2812b_set_rgb ( led , 0 , 0 , 0 ) ;
2020-03-06 11:11:53 +01:00
}
2020-06-21 09:59:52 +02:00
// start LED animation
if ( 1 = = button_pressed ) {
rust_animated = true ; // remember rust animation started
led_ws2812b_set_rgb ( 3 , rust_animation [ 0 ] [ 1 ] , rust_animation [ 0 ] [ 2 ] , rust_animation [ 0 ] [ 3 ] ) ; // start LED animation
led_ws2812b_set_rgb ( 4 , rust_animation [ 0 ] [ 1 ] , rust_animation [ 0 ] [ 2 ] , rust_animation [ 0 ] [ 3 ] ) ; // start LED animation
led_ws2812b_set_rgb ( 5 , rust_animation [ 0 ] [ 1 ] , rust_animation [ 0 ] [ 2 ] , rust_animation [ 0 ] [ 3 ] ) ; // start LED animation
strobe_animated = false ; // stop strobe animation
led_ws2812b_set_rgb ( 0 , 0 , 0 , 0 ) ; // switch LED off
led_ws2812b_set_rgb ( 1 , 0 , 0 , 0 ) ; // switch LED off
led_ws2812b_set_rgb ( 2 , 0 , 0 , 0 ) ; // switch LED off
} else if ( 2 = = button_pressed ) {
strobe_animated = true ; // remember strobe animation started
led_ws2812b_set_rgb ( 0 , strobe_animation [ 0 ] [ 1 ] , strobe_animation [ 0 ] [ 2 ] , strobe_animation [ 0 ] [ 3 ] ) ; // start LED animation
led_ws2812b_set_rgb ( 1 , strobe_animation [ 0 ] [ 1 ] , strobe_animation [ 0 ] [ 2 ] , strobe_animation [ 0 ] [ 3 ] ) ; // start LED animation
led_ws2812b_set_rgb ( 2 , strobe_animation [ 0 ] [ 1 ] , strobe_animation [ 0 ] [ 2 ] , strobe_animation [ 0 ] [ 3 ] ) ; // start LED animation
rust_animated = false ; // stop rust animation
led_ws2812b_set_rgb ( 3 , 0 , 0 , 0 ) ; // switch LED off
led_ws2812b_set_rgb ( 4 , 0 , 0 , 0 ) ; // switch LED off
led_ws2812b_set_rgb ( 5 , 0 , 0 , 0 ) ; // switch LED off
2020-03-06 11:11:53 +01:00
}
2020-06-21 09:59:52 +02:00
led_animation_ticks = 0 ; // reset timer counter
animation_progress = 0 ; // reset animation
timer_set_counter ( TIM ( LED_ANIMATION_TIMER ) , 0 ) ; // reset timer counter to get right duration
timer_enable_counter ( TIM ( LED_ANIMATION_TIMER ) ) ; // start timer
// store button
if ( button_input < LENGTH ( button_pattern ) ) {
button_pattern [ button_input + + ] = button_pressed ;
printf ( " button sequence: %u/%u \n " , button_input , pattern_length ) ;
last_button_action = rtc_get_counter_val ( ) ; // remember last button action
}
// compare pattern
if ( button_input > = pattern_length ) {
bool pattern_valid = true ;
for ( uint8_t i = 0 ; i < pattern_length ; i + + ) {
if ( button_pattern [ i ] ! = opening_settings . button_pattern [ i ] ) {
pattern_valid = false ;
break ;
}
}
// if the correct pattern has been input, press button
if ( pattern_valid ) {
puts ( " button sequence valid \n " ) ;
gpio_set ( GPIO_PORT ( RELAY_BUTTON_PIN ) , GPIO_PIN ( RELAY_BUTTON_PIN ) ) ; // set high to activate relay an simulate button press
sleep_ms ( 1000 ) ; // hold button a bit
gpio_clear ( GPIO_PORT ( RELAY_BUTTON_PIN ) , GPIO_PIN ( RELAY_BUTTON_PIN ) ) ; // set low to deactivate relay and release button
}
button_input = 0 ; // restart from scratch
last_button_action = 0 ; // restart sequence
}
} else { // ignore all button entry when not within the opening hours
button_input = 0 ;
last_button_action = 0 ;
2020-03-06 11:11:53 +01:00
}
2020-06-21 09:59:52 +02:00
// wait until both buttons are released
while ( ! gpio_get ( GPIO_PORT ( BUTTON1_PIN ) , GPIO_PIN ( BUTTON1_PIN ) ) | | ! gpio_get ( GPIO_PORT ( BUTTON2_PIN ) , GPIO_PIN ( BUTTON2_PIN ) ) ) {
//iwdg_reset(); // kick the dog
sleep_ms ( 100 ) ;
}
} // button_pressed
2020-03-06 11:11:53 +01:00
button_pressed = 0 ; // reset button pressed
2016-08-14 21:02:38 +02:00
button_flag = false ; // reset flag
2016-02-18 10:39:08 +01:00
}
2019-06-18 17:45:53 +02:00
if ( rtc_internal_tick_flag ) { // the internal RTC ticked
2016-08-14 21:02:38 +02:00
rtc_internal_tick_flag = false ; // reset flag
2017-06-27 15:44:03 +02:00
action = true ; // action has been performed
2020-03-06 11:11:53 +01:00
if ( 0 = = ( rtc_get_counter_val ( ) % RTC_TICKS_SECOND ) ) { // one second has passed
2020-02-02 21:19:03 +01:00
led_toggle ( ) ; // toggle LED (good to indicate if main function is stuck)
}
2020-03-06 11:11:53 +01:00
if ( last_button_action & & last_button_action + 5 * RTC_TICKS_SECOND < = rtc_get_counter_val ( ) ) { // pattern entry timeout
puts ( " button sequence entry timeout \n " ) ;
last_button_action = 0 ; // reset last button time
button_input = 0 ; // reset pattern input
}
// always enforce the right state
2020-06-21 09:59:52 +02:00
gpio_clear ( GPIO_PORT ( RELAY_BUTTON_PIN ) , GPIO_PIN ( RELAY_BUTTON_PIN ) ) ; // set low to not simulate button press
2020-03-06 11:11:53 +01:00
// verify if day matches
const time_t time_rtc = rtc_get_counter_val ( ) / RTC_TICKS_SECOND + rtc_offset ; // get time from internal RTC
const struct tm * time_tm = localtime ( & time_rtc ) ; // convert time
const uint16_t current_time = time_tm - > tm_hour * 60 + time_tm - > tm_min ; // get time of day in minutes
const uint8_t day = 6 - ( ( time_tm - > tm_wday + 6 ) % 7 ) ; // get bit for the current day of week
if ( opening_settings . stop_time > opening_settings . start_time ) { // stop time is on same day
opening_apply = ( ( opening_settings . days & ( 1 < < day ) ) & & current_time > opening_settings . start_time & & current_time < opening_settings . stop_time ) ;
} else { // stop time is on next day
opening_apply = ( ( opening_settings . days & ( 1 < < day ) ) & & current_time > opening_settings . start_time ) | | ( opening_settings . days & ( 1 < < ( day + 1 % 7 ) ) & & current_time < opening_settings . stop_time ) ;
}
if ( opening_apply ) { // we are in the opening hours
2020-06-21 09:59:52 +02:00
//puts("apply\n");
gpio_set ( GPIO_PORT ( RELAY_PANEL_PIN ) , GPIO_PIN ( RELAY_PANEL_PIN ) ) ; // set high to activate relay and disconnect button
2020-03-06 11:11:53 +01:00
} else {
2020-06-21 09:59:52 +02:00
gpio_clear ( GPIO_PORT ( RELAY_PANEL_PIN ) , GPIO_PIN ( RELAY_PANEL_PIN ) ) ; // set high to release relay and connect button
}
}
if ( led_animation_ticks ) { // an LED animation is running
if ( rust_animated ) {
if ( animation_progress < LENGTH ( rust_animation ) ) {
if ( led_animation_ticks > = rust_animation [ animation_progress ] [ 0 ] ) {
animation_progress + + ; // got to next animation step
led_animation_ticks = 0 ; // reset time ticks
if ( animation_progress < LENGTH ( rust_animation ) ) { // next step of animation reached
led_ws2812b_set_rgb ( 3 , rust_animation [ animation_progress ] [ 1 ] , rust_animation [ animation_progress ] [ 2 ] , rust_animation [ animation_progress ] [ 3 ] ) ;
led_ws2812b_set_rgb ( 4 , rust_animation [ animation_progress ] [ 1 ] , rust_animation [ animation_progress ] [ 2 ] , rust_animation [ animation_progress ] [ 3 ] ) ;
led_ws2812b_set_rgb ( 5 , rust_animation [ animation_progress ] [ 1 ] , rust_animation [ animation_progress ] [ 2 ] , rust_animation [ animation_progress ] [ 3 ] ) ;
} else { // end of animation reached
timer_disable_counter ( TIM ( LED_ANIMATION_TIMER ) ) ; // stop timer
led_ws2812b_set_rgb ( 3 , 0 , 0 , 0 ) ; // switch off LED
led_ws2812b_set_rgb ( 4 , 0 , 0 , 0 ) ; // switch off LED
led_ws2812b_set_rgb ( 5 , 0 , 0 , 0 ) ; // switch off LED
}
}
} else { // end of animation reached
led_animation_ticks = 0 ; // disable check
timer_disable_counter ( TIM ( LED_ANIMATION_TIMER ) ) ; // stop timer
}
}
if ( strobe_animated ) {
if ( animation_progress < LENGTH ( strobe_animation ) ) {
if ( led_animation_ticks > = strobe_animation [ animation_progress ] [ 0 ] ) {
animation_progress + + ; // got to next animation step
led_animation_ticks = 0 ; // reset time ticks
if ( animation_progress < LENGTH ( strobe_animation ) ) { // next step of animation reached
led_ws2812b_set_rgb ( 0 , strobe_animation [ animation_progress ] [ 1 ] , strobe_animation [ animation_progress ] [ 2 ] , strobe_animation [ animation_progress ] [ 3 ] ) ;
led_ws2812b_set_rgb ( 1 , strobe_animation [ animation_progress ] [ 1 ] , strobe_animation [ animation_progress ] [ 2 ] , strobe_animation [ animation_progress ] [ 3 ] ) ;
led_ws2812b_set_rgb ( 2 , strobe_animation [ animation_progress ] [ 1 ] , strobe_animation [ animation_progress ] [ 2 ] , strobe_animation [ animation_progress ] [ 3 ] ) ;
} else { // end of animation reached
timer_disable_counter ( TIM ( LED_ANIMATION_TIMER ) ) ; // stop timer
led_ws2812b_set_rgb ( 3 , 0 , 0 , 0 ) ; // switch off LED
led_ws2812b_set_rgb ( 4 , 0 , 0 , 0 ) ; // switch off LED
led_ws2812b_set_rgb ( 5 , 0 , 0 , 0 ) ; // switch off LED
}
}
} else { // end of animation reached
led_animation_ticks = 0 ; // disable check
timer_disable_counter ( TIM ( LED_ANIMATION_TIMER ) ) ; // stop timer
}
2020-03-06 11:11:53 +01:00
}
2016-08-14 21:02:38 +02:00
}
if ( action ) { // go to sleep if nothing had to be done, else recheck for activity
2016-02-18 10:39:08 +01:00
action = false ;
} else {
__WFI ( ) ; // go to sleep
}
2016-10-23 17:42:27 +02:00
} // main loop
2016-01-17 14:54:54 +01:00
}
2016-01-29 11:25:30 +01:00
2016-08-14 21:02:38 +02:00
/** @brief interrupt service routine called when tick passed on RTC */
void rtc_isr ( void )
{
rtc_clear_flag ( RTC_SEC ) ; // clear flag
rtc_internal_tick_flag = true ; // notify to show new time
}
2020-03-06 11:11:53 +01:00
void GPIO_EXTI_ISR ( BUTTON1_PIN ) ( void ) // it's the same at BUTTON2_PIN: EXT9_5
{
if ( exti_get_flag_status ( GPIO_EXTI ( BUTTON1_PIN ) ) ) {
exti_reset_request ( GPIO_EXTI ( BUTTON1_PIN ) ) ; // reset interrupt
}
if ( exti_get_flag_status ( GPIO_EXTI ( BUTTON2_PIN ) ) ) {
exti_reset_request ( GPIO_EXTI ( BUTTON2_PIN ) ) ; // reset interrupt
}
button_flag = true ; // perform button action
}
2020-06-21 09:59:52 +02:00
/** interrupt service routine called on animation tick */
void TIM_ISR ( LED_ANIMATION_TIMER ) ( void )
{
if ( timer_get_flag ( TIM ( LED_ANIMATION_TIMER ) , TIM_SR_UIF ) ) { // update event happened
timer_clear_flag ( TIM ( LED_ANIMATION_TIMER ) , TIM_SR_UIF ) ; // clear flag
led_animation_ticks + + ; // remember one tick passed
}
}