2020-06-10 18:03:59 +02:00
/** YouGotParcel firmware
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-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
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
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
2019-09-19 11:59:02 +02:00
# if !defined(STLINKV2)
2018-02-18 15:18:42 +01:00
# include "uart.h" // USART utilities
2018-10-29 15:03:29 +01:00
# endif
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-10 18:03:59 +02:00
# include "radio_sx172x.h" // LoRa module utilities
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
*/
2020-02-02 21:19:03 +01:00
# if defined(CORE_BOARD)
2019-06-18 17:43:43 +02:00
# define RTC_DATE_TIME 1
2020-02-02 21:19:03 +01:00
# else
# define RTC_DATE_TIME 0
# endif
/** number of RTC ticks per second
* @ note use integer divider of oscillator to keep second precision
*/
# define RTC_TICKS_SECOND 4
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
* @ {
*/
2020-06-10 18:03:59 +02:00
static volatile bool rtc_internal_tick_flag = false ; /**< flag set when internal RTC ticked */
static volatile bool radio_sx172x_irq_flag = false ; /**< interrupt flag for the LoRa module */
static volatile bool keep_alive_flag = false ; /**< periodic wake up to show current status */
static volatile bool lid_flag = false ; /**< flag set when post box lid is opened */
static volatile bool door_flag = false ; /**< flag set when post box door is opened */
2016-08-14 21:02:38 +02:00
/** @} */
2020-06-10 18:03:59 +02:00
/** time before sending/checking for keep alive, in RTC ticks */
# define KEEP_ALIVE_PERIOD (RTC_TICKS_SECOND * 60 * 15)
/** the pin to decide the role of this device: if it should transmit (connected to ground), or receive constantly (open) */
# define ROLE_PIN PB8
/** DIO0 pin which will be used as interrupt from the LoRa module */
# define RADIO_SX172X_GPIO_IRQ PB6
/** frequency for the LoRa communication, in Hz */
# define LORA_FREQ 447.681E6
/** the common pin of the lid switch or LED anode */
# define LID_COMMON PB10
/** the normally open pin of the lid switch or LED cathode */
# define LID_NO PB1
/** value of message when lid is opened */
# define LID_VALUE 0xaa
/** the common pin of the door switch or LED anode */
# define DOOR_COMMON PA7
/** the normally open pin of the door switch or LED cathode */
# define DOOR_NO PB0
/** value of message when door is opened */
# define DOOR_VALUE 0x55
/** maximum number of missed messaged before we indicate communication failed */
# define MAX_MISSED 5
/** if this device will transmit or receive */
static bool role_transmit = false ;
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
2020-06-10 18:03:59 +02:00
if ( ! role_transmit | | DEBUG ) {
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
2020-06-10 18:03:59 +02:00
if ( ! role_transmit | | DEBUG ) {
usb_cdcacm_putchar ( c ) ; // send byte over USB
}
2018-04-06 17:37:17 +02:00
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
*/
static void command_bootloader ( void * argument ) ;
2020-06-10 18:03:59 +02:00
static void command_scan ( void * argument )
{
( void ) argument ; // we won't use the argument
printf ( " scanning band: \n " ) ;
const uint8_t mode = radio_sx172x_read_register ( RADIO_SX172X_REG_OP_MODE ) ; // backup original mode
uint32_t frf = ( radio_sx172x_read_register ( RADIO_SX172X_REG_FRF_MSB ) < < 8 ) + ( radio_sx172x_read_register ( RADIO_SX172X_REG_FRF_MID ) < < 8 ) + ( radio_sx172x_read_register ( RADIO_SX172X_REG_FRF_LSB ) < < 0 ) ; // backup frequency
for ( uint32_t freq = 0x690000 ; freq < 0x708000 ; freq + = 100 ) { // 0x690000 = 420 MHz, 0x708000 = 450 MHz
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , ( mode & 0xf8 ) | 1 ) ; // go to standby mode to change frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_FRF_MSB , ( uint8_t ) ( freq > > 16 ) ) ; // set frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_FRF_MID , ( uint8_t ) ( freq > > 8 ) ) ; // set frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_FRF_LSB , ( uint8_t ) ( freq > > 0 ) ) ; // set frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , ( mode & 0xf8 ) | 5 ) ; // start continuous listening
sleep_ms ( 100 ) ; // wait a be to get measurements
const int16_t rssi = - 164 + radio_sx172x_read_register ( RADIO_SX172X_REG_LORA_RSSI_VALUE ) ;
printf ( " frequency: %.03f MHz (%+x), RSSI: %d dBm \n " , freq * 32E6 / ( 1 < < 19 ) / 1E6 , freq , rssi ) ;
}
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , ( mode & 0xf8 ) | 1 ) ; // go to standby mode to change frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_FRF_MSB , ( uint8_t ) ( frf > > 16 ) ) ; // set original frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_FRF_MID , ( uint8_t ) ( frf > > 8 ) ) ; // set original frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_FRF_LSB , ( uint8_t ) ( frf > > 0 ) ) ; // set original frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , mode ) ; // start original mode
}
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
{
. shortcut = ' d ' ,
. 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 ,
. command_handler = & command_bootloader ,
} ,
2020-06-10 18:03:59 +02:00
{
. shortcut = ' s ' ,
. name = " scan " ,
. command_description = " get RSSI for RF band " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_scan ,
} ,
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-10 17:48:16 +02:00
printf ( " MCU_ID: DEV_ID=0x%03x REV_ID=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 ) ;
2020-03-15 18:25:51 +01:00
printf ( " CPUID: 0x%08x \n " , SCB_CPUID ) ;
2018-04-06 17:37:17 +02:00
}
static void command_uptime ( void * argument )
{
( void ) argument ; // we won't use the argument
2020-06-10 17:43:05 +02:00
const 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-02-17 18:08:17 +01:00
time_t time_rtc = rtc_get_counter_val ( ) / RTC_TICKS_SECOND ; // get time from internal RTC
2019-06-18 17:43:43 +02:00
struct tm * time_tm = localtime ( & time_rtc ) ; // convert time
printf ( " date: %d-%02d-%02d %02d:%02d:%02d \n " , 1900 + time_tm - > tm_year , time_tm - > tm_mon , time_tm - > tm_mday , time_tm - > tm_hour , time_tm - > tm_min , time_tm - > tm_sec ) ;
} 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
time_tm . tm_mon = strtol ( & datetime [ 5 ] , NULL , 10 ) ; // parse month
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-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
2019-06-18 17:43:43 +02:00
printf ( " date and time saved: %d-%02d-%02d %02d:%02d:%02d \n " , 1900 + time_tm . tm_year , time_tm . tm_mon , time_tm . tm_mday , time_tm . tm_hour , time_tm . tm_min , time_tm . tm_sec ) ;
}
}
# 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
}
static void command_bootloader ( void * argument )
{
( void ) argument ; // we won't use the argument
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
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
{
2020-06-10 18:03:59 +02:00
//rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
rcc_clock_setup_in_hsi_out_48mhz ( ) ; // use internal HSI at low speed to save energy, but fast enough for USB
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)
2020-06-10 18:03:59 +02:00
//iwdg_set_period_ms(WATCHDOG_PERIOD); // set independent watchdog period
//iwdg_start(); // start independent watchdog
// IMPORTANT: we can't use the watchdog because we can't disable it, it keeps running in stop mode, and we don't want to wake up every 20 second to just kick the dog.
2016-10-23 17:42:27 +02:00
# endif
2017-04-03 13:32:45 +02:00
board_setup ( ) ; // setup board
2020-06-10 18:03:59 +02:00
// check role (transmit or receive)
puts ( " device role: " ) ;
rcc_periph_clock_enable ( GPIO_RCC ( ROLE_PIN ) ) ; // enable clock for pin peripheral
gpio_set ( GPIO_PORT ( ROLE_PIN ) , GPIO_PIN ( ROLE_PIN ) ) ; // pull up to detect high when not tied to ground
gpio_set_mode ( GPIO_PORT ( ROLE_PIN ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , GPIO_PIN ( ROLE_PIN ) ) ; // set button pin to input
sleep_us ( 100 ) ; // wait a bit to settle
role_transmit = ( 0 = = gpio_get ( GPIO_PORT ( ROLE_PIN ) , GPIO_PIN ( ROLE_PIN ) ) ) ; // if tied to ground, do into transmit mode
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
2020-06-10 18:03:59 +02:00
if ( ! role_transmit | | DEBUG ) {
usb_cdcacm_setup ( ) ; // setup USB CDC ACM (for printing)
}
printf ( " \n welcome to the CuVoodoo YouGotParcel notifier: %s \n " , role_transmit ? " transmitter " : " receiver " ) ; // print welcome message
2016-01-29 00:24:49 +01:00
2019-12-21 19:26:27 +01:00
# if DEBUG
2020-06-10 18:03:59 +02:00
/*
2019-12-21 19:26:27 +01:00
// 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
}
2020-06-10 18:03:59 +02:00
*/
2019-12-21 19:26:27 +01:00
# endif
2017-01-30 09:44:51 +01:00
# if !(DEBUG)
2020-06-10 18:03:59 +02:00
/*
2017-01-30 09:44:51 +01:00
// 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
}
2020-06-10 18:03:59 +02:00
*/
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-01-03 19:41:39 +01:00
# if defined(BLUE_PILL) || defined(STLINKV2) || defined(BLASTER) // for boards without a Low Speed External oscillator
2018-10-29 14:14:32 +01:00
// note: the blue pill LSE oscillator is affected when toggling the onboard LED, thus prefer the HSE
2020-02-02 21:19:03 +01:00
rtc_auto_awake ( RCC_HSE , 8000000 / 128 / RTC_TICKS_SECOND - 1 ) ; // use High Speed External oscillator (8 MHz / 128) as RTC clock (VBAT can't be used to keep the RTC running)
2018-10-28 22:44:53 +01:00
# else // for boards with an precise Low Speed External oscillator
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)
2018-10-28 22:44:53 +01:00
# endif
2020-06-10 18:03:59 +02:00
time_start = rtc_get_counter_val ( ) ; // get start time from internal RTC
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
2020-06-10 18:03:59 +02:00
// configure the Auto-Wake-Up (AWU) using the RTC alarm
rtc_set_alarm_time ( rtc_get_counter_val ( ) + KEEP_ALIVE_PERIOD ) ; // set the alarm period
rtc_enable_alarm ( ) ; // provide RTC alarm flag (and signal for EXTI)
rtc_interrupt_enable ( RTC_ALR ) ; // enable RTC interrupt on alarm
exti_set_trigger ( EXTI17 , EXTI_TRIGGER_RISING ) ; // trigger on RTC alarm
exti_enable_request ( EXTI17 ) ; // use EXTI line to be able to wake up from stop (curious this is not needed for standby)
nvic_enable_irq ( NVIC_RTC_ALARM_IRQ ) ; // allow the alarm to interrupt
puts ( " OK \n " ) ;
// setup switch input/LED output
printf ( " setup %s: " , role_transmit ? " switches " : " LEDs " ) ;
rcc_periph_clock_enable ( GPIO_RCC ( LID_COMMON ) ) ; // enable clock for pin peripheral
gpio_set ( GPIO_PORT ( LID_COMMON ) , GPIO_PIN ( LID_COMMON ) ) ; // set high
gpio_set_mode ( GPIO_PORT ( LID_COMMON ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO_PIN ( LID_COMMON ) ) ; // set to output
rcc_periph_clock_enable ( GPIO_RCC ( DOOR_COMMON ) ) ; // enable clock for pin peripheral
gpio_set ( GPIO_PORT ( DOOR_COMMON ) , GPIO_PIN ( DOOR_COMMON ) ) ; // set high
gpio_set_mode ( GPIO_PORT ( DOOR_COMMON ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO_PIN ( DOOR_COMMON ) ) ; // set to output
rcc_periph_clock_enable ( GPIO_RCC ( LID_NO ) ) ; // enable clock for pin peripheral
rcc_periph_clock_enable ( GPIO_RCC ( DOOR_NO ) ) ; // enable clock for pin peripheral
if ( role_transmit ) {
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable alternate function clock for external interrupt
gpio_clear ( GPIO_PORT ( LID_NO ) , GPIO_PIN ( LID_NO ) ) ; // pull low to detect closing
gpio_set_mode ( GPIO_PORT ( LID_NO ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , GPIO_PIN ( LID_NO ) ) ; // set pin to input
sleep_ms ( 10 ) ; // let pin settle
exti_select_source ( GPIO_EXTI ( LID_NO ) , GPIO_PORT ( LID_NO ) ) ; // mask external interrupt of the pin only for this port
exti_set_trigger ( GPIO_EXTI ( LID_NO ) , EXTI_TRIGGER_RISING ) ; // switch goes high when activated
exti_reset_request ( GPIO_EXTI ( LID_NO ) ) ; // clear possible interrupt
exti_enable_request ( GPIO_EXTI ( LID_NO ) ) ; // enable external interrupt
nvic_enable_irq ( GPIO_NVIC_EXTI_IRQ ( LID_NO ) ) ; // enable interrupt
gpio_clear ( GPIO_PORT ( DOOR_NO ) , GPIO_PIN ( DOOR_NO ) ) ; // pull low to detect closing
gpio_set_mode ( GPIO_PORT ( DOOR_NO ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , GPIO_PIN ( DOOR_NO ) ) ; // set pin to input
sleep_ms ( 10 ) ; // let pin settle
exti_select_source ( GPIO_EXTI ( DOOR_NO ) , GPIO_PORT ( DOOR_NO ) ) ; // mask external interrupt of the pin only for this port
exti_set_trigger ( GPIO_EXTI ( DOOR_NO ) , EXTI_TRIGGER_RISING ) ; // switch goes high when activated
exti_reset_request ( GPIO_EXTI ( DOOR_NO ) ) ; // clear possible interrupt
exti_enable_request ( GPIO_EXTI ( DOOR_NO ) ) ; // enable external interrupt
nvic_enable_irq ( GPIO_NVIC_EXTI_IRQ ( DOOR_NO ) ) ; // enable interrupt
} else {
gpio_set ( GPIO_PORT ( LID_NO ) , GPIO_PIN ( LID_NO ) ) ; // set high to switch LED off
gpio_set_mode ( GPIO_PORT ( LID_NO ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO_PIN ( LID_NO ) ) ; // set to output
gpio_set ( GPIO_PORT ( DOOR_NO ) , GPIO_PIN ( DOOR_NO ) ) ; // set high to switch LED off
gpio_set_mode ( GPIO_PORT ( DOOR_NO ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO_PIN ( DOOR_NO ) ) ; // set to output
}
2020-02-19 21:16:07 +01:00
puts ( " OK \n " ) ;
2016-08-14 21:02:38 +02:00
2020-06-10 18:03:59 +02:00
// setup LoRa communication using SX172x module
printf ( " setup LoRa communication (%u.%02u MHz): " , ( uint32_t ) ( LORA_FREQ / 1E6 ) , ( uint32_t ) ( LORA_FREQ / 10E3 ) % 100 ) ;
const uint8_t mode_lora = ( 1 < < 7 ) | ( 1 < < 3 ) ; // use LoRa mode, access low frequency (433 < 800 MHz)
if ( radio_sx172x_setup ( ) ) {
radio_sx172x_reset ( ) ; // reset registers to default
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_DETECT_OPTIMIZE , ( 0 < < 7 ) | ( 0x03 < < 0 ) ) ; // set AutomaticIFOn to 0 after reset (see errata)
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , 0 ) ; // put in sleep mode to be able to change mode
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , mode_lora ) ; // set LoRa mode
radio_sx172x_write_register ( RADIO_SX172X_REG_PA_CONFIG , ( 1 < < 7 ) | ( 7 < < 4 ) | ( 15 < < 0 ) ) ; // use power amplifier (select boost, use max power and output) IMPORTANT transmission will not work with the module I used
radio_sx172x_write_register ( RADIO_SX172X_REG_LNA , ( 1 < < 5 ) ) ; // use maximum gain for LNA
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_MODEM_CONFIG_1 , ( 6 < < 4 ) | ( 4 < < 1 ) | ( 0 < < 0 ) ) ; // use lowest bandwidth (62.5 kHz using XTAL) and coding rate (4/8) to have best sensitivity (we don't care about the bandwidth) + explicit header
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_MODEM_CONFIG_2 , ( 12 < < 4 ) | ( 1 < < 2 ) ) ; // use largest spreading factor (12) to get best SNR (-20 dB), add CRC on payload
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_MODEM_CONFIG_3 , ( 1 < < 3 ) | ( 1 < < 2 ) ) ; // optimize for low data rate and use AGC for LNA
// NOTE: with these settings it take almost 2 seconds to send one byte
const uint32_t lora_frf = ( ( LORA_FREQ * ( 1 < < 19 ) ) / 32E6 ) ; // the register frequency value
radio_sx172x_write_register ( RADIO_SX172X_REG_FRF_MSB , ( uint8_t ) ( lora_frf > > 16 ) ) ; // set frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_FRF_MID , ( uint8_t ) ( lora_frf > > 8 ) ) ; // set frequency
radio_sx172x_write_register ( RADIO_SX172X_REG_FRF_LSB , ( uint8_t ) ( lora_frf > > 0 ) ) ; // set frequency
// setup interrupt
rcc_periph_clock_enable ( GPIO_RCC ( RADIO_SX172X_GPIO_IRQ ) ) ; // enable clock for GPIO port
gpio_set_mode ( GPIO_PORT ( RADIO_SX172X_GPIO_IRQ ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , GPIO_PIN ( RADIO_SX172X_GPIO_IRQ ) ) ; // set interrupt as input
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable alternate function clock for external interrupt
exti_select_source ( GPIO_EXTI ( RADIO_SX172X_GPIO_IRQ ) , GPIO_PORT ( RADIO_SX172X_GPIO_IRQ ) ) ; // mask external interrupt of the IRQ pin only for this port
exti_set_trigger ( GPIO_EXTI ( RADIO_SX172X_GPIO_IRQ ) , EXTI_TRIGGER_RISING ) ; // IRQ goes high in interrupt
exti_reset_request ( GPIO_EXTI ( RADIO_SX172X_GPIO_IRQ ) ) ; // clear possible interrupt
exti_enable_request ( GPIO_EXTI ( RADIO_SX172X_GPIO_IRQ ) ) ; // enable external interrupt
nvic_enable_irq ( GPIO_NVIC_EXTI_IRQ ( RADIO_SX172X_GPIO_IRQ ) ) ; // enable interrupt
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_IRQ_FLAGS , 0xff ) ; // clear all flags
if ( role_transmit ) {
radio_sx172x_write_register ( RADIO_SX172X_REG_DIO_MAPPING_1 , ( 1 < < 6 ) ) ; // map DIO0 for TxDone output
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , mode_lora ) ; // put in sleep mode
} else {
radio_sx172x_write_register ( RADIO_SX172X_REG_DIO_MAPPING_1 , ( 0 < < 6 ) ) ; // map DIO0 for RxDone output
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , mode_lora | 5 ) ; // start continuous listening
}
puts ( " OK \n " ) ;
} else {
puts ( " could not switch on \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
2020-06-10 18:03:59 +02:00
// blink on-board LED to indicate we started
led_off ( ) ;
for ( uint8_t i = 0 ; i < 6 ; i + + ) {
led_toggle ( ) ;
sleep_ms ( 100 ) ;
}
led_off ( ) ;
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
2020-06-10 18:03:59 +02:00
keep_alive_flag = true ; // set status over LoRa on boot
uint8_t keep_alive_missed = MAX_MISSED ; // number of times we did not get a keep alive message (start with no message received)
bool yougotparcel = false ; // if a parcel is in the post box
2016-01-29 00:24:49 +01:00
while ( true ) { // infinite loop
2020-06-10 18:03:59 +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-06-10 18:03:59 +02:00
if ( lid_flag ) {
puts ( " lid opened \n " ) ;
sleep_ms ( 100 ) ; // wait a bit to remove noise and double trigger
lid_flag = false ; // reset flag
if ( ! yougotparcel ) { // empty post box got filled
yougotparcel = true ; // remember we received a parcel
keep_alive_flag = true ; // set message
}
2016-08-14 21:02:38 +02:00
action = true ; // action has been performed
2020-06-10 18:03:59 +02:00
}
if ( door_flag ) {
puts ( " door opened \n " ) ;
2020-02-19 21:16:37 +01:00
sleep_ms ( 100 ) ; // wait a bit to remove noise and double trigger
2020-06-10 18:03:59 +02:00
door_flag = false ; // reset flag
if ( yougotparcel ) { // full post box got emptied
yougotparcel = false ; // remember we removed the parcel from the post box
keep_alive_flag = true ; // set message
}
action = true ; // action has been performed
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
2020-06-10 18:03:59 +02:00
if ( 0 = = ( rtc_get_counter_val ( ) % RTC_TICKS_SECOND ) ) { // one second has passed
//led_toggle(); // toggle LED (good to indicate if main function is stuck). don't use LED since it's used for SX172x chip select
//printf("modem status: %+05b, flags: %+08b\n", radio_sx172x_read_register(RADIO_SX172X_REG_LORA_MODEM_STAT) & 0x1f, radio_sx172x_read_register(RADIO_SX172X_REG_LORA_IRQ_FLAGS));
}
2017-06-27 15:44:03 +02:00
action = true ; // action has been performed
2020-06-10 18:03:59 +02:00
}
radio_sx172x_irq_flag = ( 0 ! = gpio_get ( GPIO_PORT ( RADIO_SX172X_GPIO_IRQ ) , GPIO_PIN ( RADIO_SX172X_GPIO_IRQ ) ) ) ; // update interrupt status
if ( radio_sx172x_irq_flag ) { // LoRa module signals activity
const uint8_t lora_flags = radio_sx172x_read_register ( RADIO_SX172X_REG_LORA_IRQ_FLAGS ) ; // read flags
if ( role_transmit & & ( lora_flags & ( 1 < < 3 ) ) ) { // transmit completed
puts ( " OK \n " ) ; // should end the TX: line start
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_IRQ_FLAGS , ( 1 < < 3 ) ) ; // clear TxDone flag
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , mode_lora ) ; // put back to sleep mode
} else if ( ! role_transmit & & ( lora_flags & ( 1 < < 6 ) ) ) { // packet has been received
const uint32_t uptime = ( rtc_get_counter_val ( ) - time_start ) / RTC_TICKS_SECOND ; // get time from internal RTC
printf ( " %u.%02u:%02u:%02u " , uptime / ( 24 * 60 * 60 ) , ( uptime / ( 60 * 60 ) ) % 24 , ( uptime / 60 ) % 60 , uptime % 60 ) ; // show time stamp
puts ( " RX: " ) ;
if ( lora_flags & ( 1 < < 5 ) ) { // CRC error
goto clear_rx ;
}
if ( ! ( lora_flags & ( 1 < < 4 ) ) ) { // header invalid
goto clear_rx ;
}
const uint8_t payload_length = radio_sx172x_read_register ( RADIO_SX172X_REG_LORA_RX_NB_BYTES ) ; // get number of bytes received
if ( 1 ! = payload_length ) { // unexpected payload length
goto clear_rx ;
}
const uint8_t payload_addr = radio_sx172x_read_register ( RADIO_SX172X_REG_LORA_FIFO_RX_CURRENT_ADDR ) ; // get address in FIFO of data received
uint8_t payload ;
radio_sx172x_read_fifo ( payload_addr , & payload , payload_length ) ; // read received data
const int8_t packet_snr = ( int8_t ) radio_sx172x_read_register ( RADIO_SX172X_REG_LORA_PKT_SNR_VALUE ) / 4 ; // read SNR value
const int16_t packet_rssi = - 164 + radio_sx172x_read_register ( RADIO_SX172X_REG_LORA_PKT_RSSI_VALUE ) ; // read RSSI value
printf ( " %+02x (SNR: %d dB, RSSI: %d dBm) " , payload , packet_snr , packet_rssi ) ;
keep_alive_missed = 0 ; // reset the missed counter
// remember if we received a parcel
if ( LID_VALUE = = payload ) {
yougotparcel = true ;
} else if ( DOOR_VALUE = = payload ) {
yougotparcel = false ;
}
clear_rx :
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_IRQ_FLAGS , ( 1 < < 6 ) | ( 1 < < 5 ) | ( 1 < < 4 ) ) ; // clear RxDone, PayloadCrcError, ValidHeader flags
const uint8_t rx_addr = radio_sx172x_read_register ( RADIO_SX172X_REG_LORA_FIFO_RX_BASE_ADDR ) ; // get start of receive buffer
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_FIFO_ADDR_PTR , rx_addr ) ; // reset receive FIFO
putc ( ' \n ' ) ;
} else {
printf ( " unhandled LoRa interrupt: %+08b \n " , lora_flags ) ;
}
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_IRQ_FLAGS , 0xff ) ; // clear all flags
radio_sx172x_irq_flag = false ; // reset notification
action = true ; // action has been performed
}
if ( keep_alive_flag ) {
keep_alive_flag = false ; // reset flag
if ( keep_alive_missed < MAX_MISSED ) {
keep_alive_missed + + ; // increase missed counter, which is reset when receiving a message
}
rtc_set_alarm_time ( rtc_get_counter_val ( ) + KEEP_ALIVE_PERIOD ) ; // reset the alarm
if ( role_transmit & & 3 ! = ( radio_sx172x_read_register ( RADIO_SX172X_REG_OP_MODE ) & 0x7 ) ) { // periodically transmit (when no already transmitting
const uint8_t tx_data = ( yougotparcel ? LID_VALUE : DOOR_VALUE ) ; // transmit which has been opened
const uint32_t uptime = ( rtc_get_counter_val ( ) - time_start ) / RTC_TICKS_SECOND ; // get time from internal RTC
printf ( " %u.%02u:%02u:%02u " , uptime / ( 24 * 60 * 60 ) , ( uptime / ( 60 * 60 ) ) % 24 , ( uptime / 60 ) % 60 , uptime % 60 ) ;
printf ( " TX: %+02x ... " , tx_data ) ;
const uint8_t fifx_tx_addr = radio_sx172x_read_register ( RADIO_SX172X_REG_LORA_FIFO_TX_BASE_ADDR ) ; // get the FIFO address to write the data
radio_sx172x_write_fifo ( fifx_tx_addr , & tx_data , 1 ) ; // write payload data to TX FIFO
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_PAYLOAD_LENGTH , 1 ) ; // indicate the payload length to be transmitted
radio_sx172x_write_register ( RADIO_SX172X_REG_LORA_IRQ_FLAGS , ( 1 < < 3 ) ) ; // clear TxDone flag
radio_sx172x_write_register ( RADIO_SX172X_REG_OP_MODE , mode_lora | 3 ) ; // start transmission
}
action = true ; // action has been performed
}
// update LED status
if ( ! role_transmit ) {
if ( keep_alive_missed > = MAX_MISSED ) {
gpio_clear ( GPIO_PORT ( LID_NO ) , GPIO_PIN ( LID_NO ) ) ; // set low to switch LED on
gpio_clear ( GPIO_PORT ( DOOR_NO ) , GPIO_PIN ( DOOR_NO ) ) ; // set low to switch LED on
} else if ( yougotparcel ) {
gpio_clear ( GPIO_PORT ( LID_NO ) , GPIO_PIN ( LID_NO ) ) ; // set low to switch LED on
gpio_set ( GPIO_PORT ( DOOR_NO ) , GPIO_PIN ( DOOR_NO ) ) ; // set high to switch LED off
} else {
gpio_set ( GPIO_PORT ( LID_NO ) , GPIO_PIN ( LID_NO ) ) ; // set high to switch LED off
gpio_clear ( GPIO_PORT ( DOOR_NO ) , GPIO_PIN ( DOOR_NO ) ) ; // set low to switch LED on
2020-02-02 21:19:03 +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 {
2020-06-10 18:03:59 +02:00
if ( role_transmit & & ! DEBUG ) { // only go to stop mode when transmitting, to save battery (the receiver needs/has a permanent power source)
puts ( " zzz \n " ) ;
uart_flush ( ) ; // wait for all communication to complete
SCB_SCR | = SCB_SCR_SLEEPDEEP ; // set deep sleep in CPU
pwr_set_stop_mode ( ) ; // clear power control
pwr_voltage_regulator_low_power_in_stop ( ) ; // save even more power at the cost of wake up time
}
__WFI ( ) ; // go to sleep (or stop mode)
SCB_SCR & = ~ SCB_SCR_SLEEPDEEP ; // stop going to deep sleep
if ( role_transmit ) {
rcc_clock_setup_in_hsi_out_48mhz ( ) ; // after exiting stop mode, default HSI RC is used as clock, we need to set it again
}
2016-02-18 10:39:08 +01:00
}
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
2020-06-10 18:03:59 +02:00
/** interrupt service routine called when tick passed or alarm triggered on RTC */
2016-08-14 21:02:38 +02:00
void rtc_isr ( void )
{
2020-06-10 18:03:59 +02:00
if ( rtc_check_flag ( RTC_SEC ) ) { // tick passed
rtc_clear_flag ( RTC_SEC ) ; // clear flag
rtc_internal_tick_flag = true ; // notify to show new time
} else if ( rtc_check_flag ( RTC_ALR ) ) { // alarm triggered
rtc_clear_flag ( RTC_ALR ) ; // clear flag
keep_alive_flag = true ; // notify user
}
}
/** interrupt service routine called when alarm triggered on RTC */
void rtc_alarm_isr ( void )
{
exti_reset_request ( EXTI17 ) ; // reset interrupt
keep_alive_flag = true ; // notify user
}
/** interrupt service routine called upon LoRa IRQ */
void GPIO_EXTI_ISR ( RADIO_SX172X_GPIO_IRQ ) ( void )
{
radio_sx172x_irq_flag = true ; // notify main loop (the IRQ lien will actually be verified by the main loop, but at least this should start the loop/wake up)
exti_reset_request ( GPIO_EXTI ( RADIO_SX172X_GPIO_IRQ ) ) ; // reset interrupt
}
/** interrupt service routine called upon post box lid being opened */
void GPIO_EXTI_ISR ( LID_NO ) ( void )
{
lid_flag = true ; // notify main loop
exti_reset_request ( GPIO_EXTI ( LID_NO ) ) ; // reset interrupt
}
/** interrupt service routine called upon post box door being opened */
void GPIO_EXTI_ISR ( DOOR_NO ) ( void )
{
door_flag = true ; // notify main loop
exti_reset_request ( GPIO_EXTI ( DOOR_NO ) ) ; // reset interrupt
2016-08-14 21:02:38 +02:00
}