diff --git a/doc/eclipse_debugger_launch/ParaMETEO-STM32L476RG.launch b/doc/eclipse_debugger_launch/ParaMETEO-STM32L476RG.launch index a23cbb6..8508e7d 100644 --- a/doc/eclipse_debugger_launch/ParaMETEO-STM32L476RG.launch +++ b/doc/eclipse_debugger_launch/ParaMETEO-STM32L476RG.launch @@ -1,6 +1,6 @@ - + diff --git a/include/io.h b/include/io.h index e9f7269..07507f6 100644 --- a/include/io.h +++ b/include/io.h @@ -24,9 +24,14 @@ void io_pwr_init(void); void io_ext_watchdog_config(void); void io_ext_watchdog_service(void); +#ifdef PARAMETEO void io_vbat_meas_init(int16_t a_coeff, int16_t b_coeff); uint16_t io_vbat_meas_get(void); +uint16_t io_vbat_meas_average(uint16_t sample); +#endif +#define VBAT_MEAS_A_COEFF 1000 +#define VBAT_MEAS_B_COEFF 95 inline void io_5v_isol_sw_enable(void) { // ParaMETEO - UC_CNTRL_VS diff --git a/include/main.h b/include/main.h index c955d59..7984c7a 100644 --- a/include/main.h +++ b/include/main.h @@ -6,7 +6,7 @@ #include "config_data.h" #define SW_VER "EA05" -#define SW_DATE "09042022" +#define SW_DATE "17042022" #define SYSTICK_TICKS_PER_SECONDS 100 #define SYSTICK_TICKS_PERIOD 10 @@ -18,6 +18,22 @@ #define OWN_APRS_MSG_LN 160 +// backup registers (ParaMETEO) +// 0 -> powersave status +// 1 -> last sleep rtc time +// 2 -> last wakeup rtc time +// 3 -> controller configuration status +// 4 -> wakeup events MSB, sleep events LSB + +#ifdef STM32L471xx +#define REGISTER RTC->BKP0R +#define REGISTER_LAST_SLEEP RTC->BKP1R +#define REGISTER_LAST_WKUP RTC->BKP2R +#define REGISTER_COUNTERS RTC->BKP4R +#define REGISTER_MONITOR RTC->BKP5R +#define REGISTER_LAST_SLTIM RTC->BKP6R +#endif + extern uint32_t master_time; extern const config_data_mode_t * main_config_data_mode; @@ -55,14 +71,40 @@ extern srl_context_t* main_gsm_srl_ctx_ptr; extern uint8_t main_kiss_enabled; +extern uint8_t main_woken_up; + extern char after_tx_lock; extern unsigned short rx10m, tx10m, digi10m, digidrop10m, kiss10m; +//void main_set_monitor(int8_t bit); + uint16_t main_get_adc_sample(void); void main_service_cpu_load_ticks(void); +/** + * Inline used to trace an execution flow across main for(;;) loop and some + * powersaving functions. In case of software fault it's value may help to trace + * at witch point the crash has occured + */ +inline void main_set_monitor(int8_t bit) { +#ifdef STM32L471xx + // enable access to backup domain + PWR->CR1 |= PWR_CR1_DBP; + + if (bit >= 0) { + REGISTER_MONITOR |= (1 << bit); + + } + else { + REGISTER_MONITOR = 0; + } + + PWR->CR1 &= (0xFFFFFFFF ^ PWR_CR1_DBP); +#endif +} + inline uint32_t main_get_master_time(void) { return master_time; } diff --git a/include/pwr_save_configuration.h b/include/pwr_save_configuration.h index dd5ee29..650adaa 100644 --- a/include/pwr_save_configuration.h +++ b/include/pwr_save_configuration.h @@ -30,7 +30,11 @@ * How long in minutes the controller will sleep in L7 state between checking * if battery has been charged. */ -#define PWR_SAVE_CUTOFF_SLEEP_TIME_IN_MINUTES 4 +#define PWR_SAVE_CUTOFF_SLEEP_TIME_IN_MINUTES 10 +/** + * Do not uncomment this on production devices + */ +//#define INHIBIT_CUTOFF #endif /* INCLUDE_PWR_SAVE_CONFIGURATION_H_ */ diff --git a/include/rte_main.h b/include/rte_main.h index 9686ebb..b57286f 100644 --- a/include/rte_main.h +++ b/include/rte_main.h @@ -15,5 +15,12 @@ extern uint8_t rte_main_trigger_modbus_status; extern uint8_t rte_main_trigger_wx_packet; extern uint16_t rte_main_battery_voltage; +extern uint16_t rte_main_average_battery_voltage; + +extern uint16_t rte_main_wakeup_count; + +extern uint16_t rte_main_going_sleep_count; + +extern uint32_t rte_main_last_sleep_master_time; #endif diff --git a/src/io.c b/src/io.c index d6275e5..01593c9 100644 --- a/src/io.c +++ b/src/io.c @@ -25,7 +25,14 @@ #if defined(PARAMETEO) LL_GPIO_InitTypeDef GPIO_InitTypeDef; -int16_t io_vbat_a_coeff, io_vbat_b_coeff; +int16_t io_vbat_a_coeff = 0, io_vbat_b_coeff = 0; + +#define VBATT_HISTORY_LN 16 +static uint16_t io_vbatt_history[VBATT_HISTORY_LN]; + +static uint8_t io_vbatt_history_it = 0; + +#define MINIMUM_SENSEFUL_VBATT_VOLTAGE 512u #endif void io_oc_init(void) { @@ -154,10 +161,13 @@ void io_ext_watchdog_service(void) { void io_vbat_meas_init(int16_t a_coeff, int16_t b_coeff) { -#ifdef STM32L471xx - io_vbat_a_coeff = a_coeff; - io_vbat_b_coeff = b_coeff; +#ifdef PARAMETEO + if (a_coeff != io_vbat_a_coeff || b_coeff != io_vbat_b_coeff) { + io_vbat_a_coeff = a_coeff; + io_vbat_b_coeff = b_coeff; + memset(io_vbatt_history, 0x00, VBATT_HISTORY_LN); + } GPIO_InitTypeDef.Mode = LL_GPIO_MODE_ANALOG; GPIO_InitTypeDef.OutputType = LL_GPIO_OUTPUT_PUSHPULL; @@ -176,8 +186,14 @@ void io_vbat_meas_init(int16_t a_coeff, int16_t b_coeff) { // RCC->AHB2ENR &= (0xFFFFFFFF ^ RCC_AHB2ENR_ADCEN); // RCC->AHB2ENR |= RCC_AHB2ENR_ADCEN; - // the adc should be disabled now, but just to be sure that this is a case - ADC2->CR &= (0xFFFFFFFF ^ ADC_CR_ADEN); + // check if ADC is enabled + if ((ADC2->CR & ADC_CR_ADEN) != 0) { + // disable it + ADC2->CR |= ADC_CR_ADDIS; + + // and wait for disabling to complete + while((ADC2->CR & ADC_CR_ADDIS) == ADC_CR_ADDIS); + } // exit from deep-power-down mode ADC2->CR &= (0xFFFFFFFF ^ ADC_CR_DEEPPWD); @@ -211,37 +227,93 @@ void io_vbat_meas_init(int16_t a_coeff, int16_t b_coeff) { #endif } -uint16_t io_vbat_meas_get(void) { +/** + * This function will measure current B+ voltage using ADC and return + * either average (if 0) or current / momentary value (non zero) + */ +uint16_t io_vbat_meas_get() { uint16_t out = 0; -#ifdef STM32L471xx + +#ifdef PARAMETEO float temp = 0.0f; - // start ADC - ADC2->CR |= ADC_CR_ADEN; + // if ADC is not enabled + if ((ADC2->CR & ADC_CR_ADEN) == 0) { + // start ADC + ADC2->CR |= ADC_CR_ADEN; - // wait for startup - while((ADC2->ISR & ADC_ISR_ADRDY) == 0); + // wait for startup + while((ADC2->ISR & ADC_ISR_ADRDY) == 0); + } // start conversion ADC2->CR |= ADC_CR_ADSTART; // wait for conversion to finish - while((ADC2->ISR & ADC_ISR_EOC) != ADC_ISR_EOC) { - ; + while((ADC2->ISR & ADC_ISR_EOC) == 0) { + if ((ADC2->ISR & ADC_ISR_EOS) != 0) { + break; + } } + // get conversion result out = ADC2->DR; - // disable ADC - ADC2->CR &= (0xFFFFFFFF ^ ADC_CR_ADEN); + // if end of sequence flag is not cleared + if ((ADC2->ISR & ADC_ISR_EOS) != 0) { + ADC2->ISR |= ADC_ISR_EOS; + } + + // disable conversion + ADC2->CR |= ADC_CR_ADSTP; // adc has a resulution of 12bit, so with VDDA of 3.3V it gives about .00081V per division temp = (float)out * 0.00081f; // real voltage on ADC input out = (uint16_t) (temp * (float)io_vbat_a_coeff + (float)io_vbat_b_coeff); + #endif return out; } +uint16_t io_vbat_meas_average(uint16_t sample) { + + uint16_t out = 0; + + int i = 0; + + uint32_t average_acc = 0; + + // if the iterator reached the end of buffer + if (io_vbatt_history_it >= VBATT_HISTORY_LN) { + // reset it to the begining + io_vbatt_history_it = 0; + } + + // but new sample in the buffer + io_vbatt_history[io_vbatt_history_it++] = sample; + + + for (i = 0; i < VBATT_HISTORY_LN; i++) { + + // break from the loop if buffer isn't fully filled with data + if (io_vbatt_history[i] < MINIMUM_SENSEFUL_VBATT_VOLTAGE) { + break; + } + + // sum sample + average_acc += io_vbatt_history[i]; + } + + // if whole buffer has been used for average calculation + if (i >= VBATT_HISTORY_LN) { + // replace output + out = (uint16_t)(average_acc / VBATT_HISTORY_LN); + } + + + return out; +} + diff --git a/src/it_handlers.c b/src/it_handlers.c index 2c18213..dfafe99 100644 --- a/src/it_handlers.c +++ b/src/it_handlers.c @@ -89,6 +89,8 @@ void it_handlers_set_priorities(void) { #ifdef STM32L471xx void RTC_WKUP_IRQHandler(void) { + main_woken_up = 1; + // clear pending interrupt NVIC_ClearPendingIRQ(RTC_WKUP_IRQn); @@ -96,14 +98,12 @@ void RTC_WKUP_IRQHandler(void) { EXTI->PR1 |= EXTI_PR1_PIF20; + main_set_monitor(12); + system_clock_configure_l4(); pwr_save_exit_from_stop2(); - rte_main_battery_voltage = io_vbat_meas_get(); - - pwr_save_pooling_handler(main_config_data_mode, main_config_data_basic, 1, rte_main_battery_voltage); - } #endif diff --git a/src/main.c b/src/main.c index 0e89b73..1e4d1a4 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,8 @@ #include "gsm/sim800c_engineering.h" #include "gsm/sim800c_poolers.h" #include "gsm/sim800c_gprs.h" +#include "http_client/http_client.h" + #include "aprsis.h" #endif @@ -101,7 +103,11 @@ // backup registers (ParaMETEO) // 0 -> powersave status +// 1 -> last sleep rtc time +// 2 -> last wakeup rtc time // 3 -> controller configuration status +// 4 -> wakeup events MSB, sleep events LSB +// 5 -> monitor #define CONFIG_FIRST_RESTORED (1) @@ -220,10 +226,6 @@ umb_retval_t main_umb_retval = UMB_UNINITIALIZED; // result of CRC calculation uint32_t main_crc_result = 0; -char after_tx_lock; - -unsigned short rx10m = 0, tx10m = 0, digi10m = 0, digidrop10m = 0, kiss10m = 0; - #if defined(STM32L471xx) LL_GPIO_InitTypeDef GPIO_InitTypeDef; @@ -231,6 +233,14 @@ gsm_sim800_state_t main_gsm_state; #endif +#if defined(PARAMETEO) +uint8_t main_woken_up = 0; +#endif + +char after_tx_lock; + +unsigned short rx10m = 0, tx10m = 0, digi10m = 0, digidrop10m = 0, kiss10m = 0; + static void message_callback(struct AX25Msg *msg) { } @@ -497,7 +507,7 @@ int main(int argc, char* argv[]){ pwr_save_init(main_config_data_mode->powersave); // initialize B+ measurement - io_vbat_meas_init(1000, 95); + io_vbat_meas_init(VBAT_MEAS_A_COEFF, VBAT_MEAS_B_COEFF); #endif // initalizing separated Open Collector output @@ -712,7 +722,7 @@ int main(int argc, char* argv[]){ main_wx_srl_ctx_ptr->te_pin = GPIO_Pin_8; main_wx_srl_ctx_ptr->te_port = GPIOA; #endif -#if defined(PARAMETEO) +#if defined(STM32L471xx) main_wx_srl_ctx_ptr->te_pin = LL_GPIO_PIN_8; main_wx_srl_ctx_ptr->te_port = GPIOA; @@ -941,8 +951,7 @@ int main(int argc, char* argv[]){ if (main_config_data_mode->gsm == 1) { gsm_sim800_init(&main_gsm_state, 1); - http_client_init(&main_gsm_state, main_gsm_srl_ctx_ptr, 0); - + //http_client_init(&main_gsm_state, main_gsm_srl_ctx_ptr, 0); //aprsis_init(&main_gsm_srl_ctx, &main_gsm_state, "SP8EBC", 10, 23220); } #endif @@ -954,11 +963,16 @@ int main(int argc, char* argv[]){ beacon_send_own(0); #endif + delay_fixed(1500); + + telemetry_send_status_powersave_registers(REGISTER_LAST_SLEEP, REGISTER_LAST_WKUP, REGISTER_COUNTERS, REGISTER_MONITOR, REGISTER_LAST_SLTIM); } // Infinite loop while (1) { + main_set_monitor(-1); + // incrementing current cpu ticks main_current_cpu_idle_ticks++; @@ -969,6 +983,7 @@ int main(int argc, char* argv[]){ ; } + main_set_monitor(0); #if defined(PARATNC_HWREV_A) || defined(PARATNC_HWREV_B) || defined(PARATNC_HWREV_C) // read the state of a button input if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) { @@ -1003,6 +1018,24 @@ int main(int argc, char* argv[]){ } #endif +#if defined(PARAMETEO) + if (main_woken_up == 1) { +// io_vbat_meas_init(VBAT_MEAS_A_COEFF, VBAT_MEAS_B_COEFF); + + rte_main_battery_voltage = io_vbat_meas_get(); + + // reinitialize APRS radio modem to clear all possible intermittent state caused by + // switching power state in the middle of reception APRS packet + ax25_new_msg_rx_flag = 0; + main_ax25.dcd = false; + + main_woken_up = 0; + + main_set_monitor(1); + } +#endif + + main_set_monitor(11); // if new packet has been received from radio channel if(ax25_new_msg_rx_flag == 1) { @@ -1135,11 +1168,19 @@ int main(int argc, char* argv[]){ rtu_serial_pool(); } + main_set_monitor(2); + // get all meteo measuremenets each 65 seconds. some values may not be // downloaded from sensors if _METEO and/or _DALLAS_AS_TELEM aren't defined if (main_wx_sensors_pool_timer < 10) { rte_main_battery_voltage = io_vbat_meas_get(); + rte_main_average_battery_voltage = io_vbat_meas_average(rte_main_battery_voltage); + + // meas average will return 0 if internal buffer isn't filled completely + if (rte_main_average_battery_voltage == 0) { + rte_main_average_battery_voltage = rte_main_battery_voltage; + } if (main_modbus_rtu_master_enabled == 1) { rtu_serial_start(); @@ -1171,19 +1212,31 @@ int main(int argc, char* argv[]){ } + main_set_monitor(3); + main_wx_sensors_pool_timer = 65500; } if (main_one_minute_pool_timer < 10) { + main_set_monitor(4); + #ifndef _MUTE_OWN packet_tx_handler(main_config_data_basic, main_config_data_mode); #endif + main_set_monitor(5); + #ifdef STM32L471xx if (main_config_data_mode->gsm == 1) { - gsm_sim800_poolers_one_minute(main_gsm_srl_ctx_ptr, &main_gsm_state); + gsm_sim800_poolers_one_minute(main_gsm_srl_ctx_ptr, &main_gsm_state); + + +// if (gsm_sim800_gprs_ready == 1) { +// retval = http_client_async_get("http://pogoda.cc:8080/meteo_backend/status", strlen("http://pogoda.cc:8080/meteo_backend/status"), 0xFFF0, 0x1, 0); +// } + } #endif @@ -1192,7 +1245,7 @@ int main(int argc, char* argv[]){ if (main_one_second_pool_timer < 10) { - //digi_pool_viscous(); + main_set_monitor(6); digi_pool_viscous(); @@ -1221,6 +1274,8 @@ int main(int argc, char* argv[]){ analog_anemometer_direction_handler(); } + main_set_monitor(7); + main_one_second_pool_timer = 1000; } else if (main_one_second_pool_timer < -10) { @@ -1247,6 +1302,8 @@ int main(int argc, char* argv[]){ if (main_ten_second_pool_timer < 10) { + main_set_monitor(8); + if (rte_main_trigger_wx_packet == 1) { packet_tx_send_wx_frame(); @@ -1256,13 +1313,17 @@ int main(int argc, char* argv[]){ #ifdef PARAMETEO // inhibit any power save switching when modem transmits data - if (!main_afsk.sending) { + if (!main_afsk.sending && main_woken_up == 0) { pwr_save_pooling_handler(main_config_data_mode, main_config_data_basic, packet_tx_get_minutes_to_next_wx(), rte_main_battery_voltage); } #endif + main_set_monitor(9); + #ifdef STM32L471xx if (main_config_data_mode->gsm == 1) { + // TODO + // retval = aprsis_connect_and_login(TEST_IP, strlen(TEST_IP), 14580); // // if (retval == 0) { @@ -1295,6 +1356,8 @@ int main(int argc, char* argv[]){ main_ten_second_pool_timer = 10000; } + main_set_monitor(10); + } // Infinite loop, never return. diff --git a/src/packet_tx_handler.c b/src/packet_tx_handler.c index 2efac96..983ec21 100644 --- a/src/packet_tx_handler.c +++ b/src/packet_tx_handler.c @@ -140,6 +140,15 @@ void packet_tx_handler(const config_data_basic_t * const config_basic, const con SendWXFrame(rte_wx_average_windspeed, rte_wx_max_windspeed, rte_wx_average_winddirection, rte_wx_temperature_average_external_valid, rte_wx_pressure_valid, rte_wx_humidity_valid); + /** + * debug + * + * #define REGISTER_LAST_SLEEP RTC->BKP1R + #define REGISTER_LAST_WKUP RTC->BKP2R + #define REGISTER_COUNTERS RTC->BKP4R + * + */ + #ifdef EXTERNAL_WATCHDOG io_ext_watchdog_service(); #endif @@ -209,6 +218,8 @@ void packet_tx_handler(const config_data_basic_t * const config_basic, const con if (packet_tx_telemetry_counter >= packet_tx_telemetry_interval) { + telemetry_send_status_powersave_registers(REGISTER_LAST_SLEEP, REGISTER_LAST_WKUP, REGISTER_COUNTERS, REGISTER_MONITOR, REGISTER_LAST_SLTIM); + packet_tx_multi_per_call_handler(); // if there weren't any erros related to communication with DS12B20 from previous function call @@ -301,7 +312,6 @@ void packet_tx_handler(const config_data_basic_t * const config_basic, const con rte_wx_wind_qf = AN_WIND_QF_UNKNOWN; } - if (config_mode->victron == 1) { telemetry_send_values_pv(rx10m, digi10m, rte_pv_battery_current, rte_pv_battery_voltage, rte_pv_cell_voltage, dallas_qf, pressure_qf, humidity_qf, wind_qf); } @@ -314,11 +324,11 @@ void packet_tx_handler(const config_data_basic_t * const config_basic, const con // if _METEO will be enabled, but without _DALLAS_AS_TELEM the fifth channel will be used to transmit temperature from MS5611 // which may be treated then as 'rack/cabinet internal temperature'. Dallas DS12B10 will be used for ragular WX frames - telemetry_send_values(rx10m, tx10m, digi10m, rte_main_battery_voltage, digidrop10m, rte_wx_temperature_internal_valid, dallas_qf, pressure_qf, humidity_qf, wind_qf, pwr_save_currently_cutoff, config_mode); + telemetry_send_values(rx10m, tx10m, digi10m, rte_main_average_battery_voltage, digidrop10m, rte_wx_temperature_internal_valid, dallas_qf, pressure_qf, humidity_qf, wind_qf, pwr_save_currently_cutoff, config_mode); } else { // if user will disable both _METEO and _DALLAS_AS_TELEM value will be zeroed internally anyway - telemetry_send_values(rx10m, tx10m, digi10m, rte_main_battery_voltage, digidrop10m, 0.0f, dallas_qf, pressure_qf, humidity_qf, wind_qf, pwr_save_currently_cutoff, config_mode); + telemetry_send_values(rx10m, tx10m, digi10m, rte_main_average_battery_voltage, digidrop10m, 0.0f, dallas_qf, pressure_qf, humidity_qf, wind_qf, pwr_save_currently_cutoff, config_mode); } #else // if _DALLAS_AS_TELEM will be enabled the fifth channel will be set to temperature measured by DS12B20 diff --git a/src/pwr_save.c b/src/pwr_save.c index 42629ae..5a02d83 100644 --- a/src/pwr_save.c +++ b/src/pwr_save.c @@ -18,14 +18,12 @@ #include "packet_tx_handler.h" #include "wx_handler.h" #include "main.h" +#include "telemetry.h" #include "rte_main.h" #include "drivers/analog_anemometer.h" - -#define REGISTER RTC->BKP0R - #define INHIBIT_PWR_SWITCH_PERIODIC_H 1 #define IN_STOP2_MODE (1 << 1) #define IN_C0_STATE (1 << 2) @@ -51,6 +49,12 @@ int16_t pwr_save_sleep_time_in_seconds = -1; */ int8_t pwr_save_currently_cutoff = 0; +/** + * This stores a previous value of 'pwr_save_currently_cutoff' which is required to + * trigger a status message when controller goes into low battery voltage or cutoff state + */ +int8_t pwr_save_previously_cutoff = 0; + /** * This is cutoff voltage at which the power saving subsystem will keep ParaMETEO constantly * in L7 mode and wakeup once every 20 minutes to check B+ once again @@ -160,7 +164,13 @@ void pwr_save_init(config_data_powersave_mode_t mode) { */ void pwr_save_enter_stop2(void) { - rte_main_battery_voltage = io_vbat_meas_get(); + uint16_t counter = 0; + + // set 31st monitor bit + main_set_monitor(31); + + // clear main battery voltage to be sure that it'd be updated??? + rte_main_battery_voltage = 0; analog_anemometer_deinit(); @@ -174,11 +184,22 @@ void pwr_save_enter_stop2(void) { RTC->WPR = 0xCA; RTC->WPR = 0x53; + // unlock an access to backup domain pwr_save_unclock_rtc_backup_regs(); // save an information that STOP2 mode has been applied RTC->BKP0R |= IN_STOP2_MODE; + // save a timestamp when micro has been switched to STOP2 mode + REGISTER_LAST_SLEEP = RTC->TR; + + // increment the STOP2 sleep counters + counter = (uint16_t)(REGISTER_COUNTERS & 0xFFFF); + + counter++; + + REGISTER_COUNTERS = (REGISTER_COUNTERS & 0xFFFF0000) | counter; + pwr_save_lock_rtc_backup_regs(); SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; @@ -198,6 +219,31 @@ void pwr_save_enter_stop2(void) { */ void pwr_save_exit_from_stop2(void) { + uint32_t counter = 0; + + // set 30th minitor bit + main_set_monitor(30); + + // unlock access to backup registers + pwr_save_unclock_rtc_backup_regs(); + + // save a timestamp of this wakeup event + REGISTER_LAST_WKUP = RTC->TR; + + // increase wakeup counter + counter = (uint32_t)((REGISTER_COUNTERS & 0xFFFF0000) >> 16); + + counter++; + + // check counter overflow conditions + if (counter > 0xFFFF) { + counter = 0; + } + + REGISTER_COUNTERS = (REGISTER_COUNTERS & 0x0000FFFF) | (counter << 16); + + pwr_save_lock_rtc_backup_regs(); + // packet tx timers values packet_tx_counter_values_t timers; @@ -240,6 +286,8 @@ void pwr_save_exit_from_stop2(void) { default: break; } + + main_set_monitor(29); } int pwr_save_switch_mode_to_c0(void) { @@ -450,6 +498,8 @@ void pwr_save_switch_mode_to_l6(uint16_t sleep_time) { return; } + main_set_monitor(28); + // turn OFF +5V_S (and internal VHF radio module in HW-RevB) io___cntrl_vbat_s_disable(); @@ -471,6 +521,8 @@ void pwr_save_switch_mode_to_l6(uint16_t sleep_time) { // set for C3 mode REGISTER |= IN_L6_STATE; + REGISTER_LAST_SLTIM = sleep_time; + // lock access to backup pwr_save_lock_rtc_backup_regs(); @@ -485,6 +537,8 @@ void pwr_save_switch_mode_to_l6(uint16_t sleep_time) { pwr_save_enter_stop2(); + main_set_monitor(27); + } @@ -494,6 +548,8 @@ void pwr_save_switch_mode_to_l7(uint16_t sleep_time) { return; } + main_set_monitor(26); + // turn OFF +5V_S (and internal VHF radio module in HW-RevB) io___cntrl_vbat_s_disable(); @@ -515,6 +571,8 @@ void pwr_save_switch_mode_to_l7(uint16_t sleep_time) { // set for C3 mode REGISTER |= IN_L7_STATE; + REGISTER_LAST_SLTIM = sleep_time; + // lock access to backup pwr_save_lock_rtc_backup_regs(); @@ -529,6 +587,8 @@ void pwr_save_switch_mode_to_l7(uint16_t sleep_time) { led_control_led2_bottom(false); pwr_save_enter_stop2(); + + main_set_monitor(25); } void pwr_save_pooling_handler(const config_data_mode_t * config, const config_data_basic_t * timers, int16_t minutes_to_wx, uint16_t vbatt) { @@ -541,6 +601,11 @@ void pwr_save_pooling_handler(const config_data_mode_t * config, const config_da // by default use powersave mode from controller configuration config_data_powersave_mode_t psave_mode = config->powersave; + main_set_monitor(24); + + // save previous state + pwr_save_previously_cutoff = pwr_save_currently_cutoff; + // check if battery voltage measurement is done and senseful if (vbatt < MINIMUM_SENSEFUL_VBATT_VOLTAGE) { // inhibit both cutoff and aggresive powersave if vbatt measurement is either not @@ -548,21 +613,44 @@ void pwr_save_pooling_handler(const config_data_mode_t * config, const config_da vbatt = 0xFFFFu; } - // check if battery voltage is below low voltage level - if (vbatt <= pwr_save_aggressive_powersave_voltage) { - // if battery voltage is low swtich to aggressive powersave mode - pwr_save_currently_cutoff |= CURRENTLY_VBATT_LOW; + #ifdef INHIBIT_CUTOFF + vbatt = 0xFFFFu; + #endif - psave_mode = PWSAVE_AGGRESV; + if (vbatt > PWR_SAVE_STARTUP_RESTORE_VOLTAGE_DEF) { + pwr_save_currently_cutoff = 0; + + main_set_monitor(23); } else { - pwr_save_currently_cutoff &= (0xFF ^ CURRENTLY_VBATT_LOW); + if (vbatt <= PWR_SAVE_CUTOFF_VOLTAGE_DEF) { + main_set_monitor(22); + + // if the battery voltage is below cutoff level and the ParaMETEO controller is currently not cut off + pwr_save_currently_cutoff |= CURRENTLY_CUTOFF; + } + + // check if battery voltage is below low voltage level + if (vbatt <= PWR_SAVE_AGGRESIVE_POWERSAVE_VOLTAGE) { + main_set_monitor(21); + + // if battery voltage is low swtich to aggressive powersave mode + pwr_save_currently_cutoff |= CURRENTLY_VBATT_LOW; + + } + + } + + main_set_monitor(20); + + // check if cutoff status has changed + if (pwr_save_currently_cutoff != pwr_save_previously_cutoff) { + telemetry_send_status_powersave_cutoff(vbatt, pwr_save_previously_cutoff, pwr_save_currently_cutoff); } - if (vbatt <= pwr_save_cutoff_voltage && ((pwr_save_currently_cutoff & CURRENTLY_CUTOFF) == 0)) { - // if the battery voltage is below cutoff level and the ParaMETEO controller is currently not cut off - pwr_save_currently_cutoff |= CURRENTLY_CUTOFF; + if ((pwr_save_currently_cutoff & CURRENTLY_CUTOFF) != 0) { + main_set_monitor(19); // clear all previous powersave indication bits as we want to go sleep being already in L7 state pwr_save_clear_powersave_idication_bits(); @@ -571,22 +659,15 @@ void pwr_save_pooling_handler(const config_data_mode_t * config, const config_da pwr_save_switch_mode_to_l7(60 * PWR_SAVE_CUTOFF_SLEEP_TIME_IN_MINUTES); return; - } - else if (vbatt <= pwr_save_startup_restore_voltage && ((pwr_save_currently_cutoff & CURRENTLY_CUTOFF) != 0)) { - // clear all previous powersave indication bits as we want to go sleep being already in L7 state - pwr_save_clear_powersave_idication_bits(); - // if the ParaMETEO is cutted off currently but battery hasn't been charged above restore voltage - pwr_save_switch_mode_to_l7(60 * PWR_SAVE_CUTOFF_SLEEP_TIME_IN_MINUTES); + if ((pwr_save_currently_cutoff & CURRENTLY_VBATT_LOW) != 0) { + main_set_monitor(18); - return; - } - else { - // if battery level is above restore voltage and aggressive powersave voltage - pwr_save_currently_cutoff &= (0xFF ^ CURRENTLY_CUTOFF); + psave_mode = PWSAVE_AGGRESV; } + // get current counter values packet_tx_get_current_counters(&counters); @@ -705,6 +786,8 @@ void pwr_save_pooling_handler(const config_data_mode_t * config, const config_da } else { // WX if (minutes_to_wx > 2) { + main_set_monitor(17); + // if there is more than two minutes to send wx packet pwr_save_switch_mode_to_l7((timers->wx_transmit_period * 60) - 120); } @@ -747,6 +830,8 @@ void pwr_save_pooling_handler(const config_data_mode_t * config, const config_da // if stations is configured to send wx packet less often than every 5 minutes if (minutes_to_wx > 1) { + main_set_monitor(16); + // if there is more than one minute to wx packet pwr_save_switch_mode_to_l7((timers->wx_transmit_period * 60) - 60); // TODO: !!! } @@ -765,6 +850,8 @@ void pwr_save_pooling_handler(const config_data_mode_t * config, const config_da // if station is configured to sent wx packet in every 5 minutes or more often if (minutes_to_wx > 1) { + main_set_monitor(15); + pwr_save_switch_mode_to_l6((timers->wx_transmit_period * 60) - 60); // TODO: !!! } else { @@ -785,6 +872,8 @@ void pwr_save_pooling_handler(const config_data_mode_t * config, const config_da } else { // WX if (minutes_to_wx > 1) { + main_set_monitor(14); + // if there is more than one minute to send wx packet pwr_save_switch_mode_to_l7((timers->wx_transmit_period * 60) - 60); } @@ -812,6 +901,8 @@ void pwr_save_pooling_handler(const config_data_mode_t * config, const config_da } } + main_set_monitor(13); + if (reinit_sensors != 0) { // reinitialize all i2c sensors wx_force_i2c_sensor_reset = 1; diff --git a/src/rte_main.c b/src/rte_main.c index 6b92216..e8f14e0 100644 --- a/src/rte_main.c +++ b/src/rte_main.c @@ -16,6 +16,16 @@ uint8_t rte_main_trigger_modbus_status = 0; uint8_t rte_main_trigger_wx_packet = 0; + +#ifdef PARAMETEO uint16_t rte_main_battery_voltage; +uint16_t rte_main_average_battery_voltage = 0; + +uint16_t rte_main_wakeup_count = 0; + +uint16_t rte_main_going_sleep_count = 0; + +uint32_t rte_main_last_sleep_master_time = 0; +#endif diff --git a/system/include/aprs/telemetry.h b/system/include/aprs/telemetry.h index 60fdb9c..2afefdf 100644 --- a/system/include/aprs/telemetry.h +++ b/system/include/aprs/telemetry.h @@ -94,6 +94,9 @@ void telemetry_send_status(void); void telemetry_send_status_raw_values_modbus(void); +void telemetry_send_status_powersave_cutoff(uint16_t battery_voltage, int8_t vbatt_low, int8_t cutoff); +void telemetry_send_status_powersave_registers(uint32_t register_last_sleep, uint32_t register_last_wakeup, uint32_t register_counters, uint32_t monitor, uint32_t last_sleep_time); + #ifdef __cplusplus } #endif diff --git a/system/src/aprs/beacon.c b/system/src/aprs/beacon.c index e896a02..22c8c6a 100644 --- a/system/src/aprs/beacon.c +++ b/system/src/aprs/beacon.c @@ -20,7 +20,7 @@ void beacon_send_own(uint16_t voltage) { main_own_aprs_msg_len = sprintf(main_own_aprs_msg, "=%s%c%c%s%c%c %s", main_string_latitude, main_config_data_basic->n_or_s, main_symbol_f, main_string_longitude, main_config_data_basic->e_or_w, main_symbol_s, main_config_data_basic->comment); } else { - main_own_aprs_msg_len = sprintf(main_own_aprs_msg, "=%s%c%c%s%c%c %s Vbatt %dV", main_string_latitude, main_config_data_basic->n_or_s, main_symbol_f, main_string_longitude, main_config_data_basic->e_or_w, main_symbol_s, main_config_data_basic->comment, (int)voltage); + main_own_aprs_msg_len = sprintf(main_own_aprs_msg, "=%s%c%c%s%c%c %s [Vbatt: %dV]", main_string_latitude, main_config_data_basic->n_or_s, main_symbol_f, main_string_longitude, main_config_data_basic->e_or_w, main_symbol_s, main_config_data_basic->comment, (int)voltage); } main_own_aprs_msg[main_own_aprs_msg_len] = 0; ax25_sendVia(&main_ax25, main_own_path, main_own_path_ln, main_own_aprs_msg, main_own_aprs_msg_len); diff --git a/system/src/aprs/telemetry.c b/system/src/aprs/telemetry.c index ce20e12..b330a13 100644 --- a/system/src/aprs/telemetry.c +++ b/system/src/aprs/telemetry.c @@ -19,6 +19,16 @@ uint16_t telemetry_counter = 0; +#ifdef PARAMETEO +#include "pwr_save.h" + +const char * telemetry_vbatt_normal = "VBATT_GOOD"; +const char * telemetry_vbatt_low = "VBATT_LOW"; +const char * telemetry_vbatt_cutoff = "VBATT_CUTOFF"; +const char * telemetry_vbatt_unknown = "VBATT_UNKNOWN"; + +#endif + void telemetry_send_chns_description_pv(const config_data_basic_t * const config_basic) { // a buffer to assembly the 'call-ssid' string at the begining of the frame @@ -549,5 +559,56 @@ void telemetry_send_status_raw_values_modbus(void) { #endif } +void telemetry_send_status_powersave_cutoff(uint16_t battery_voltage, int8_t previous_cutoff, int8_t current_cutoff) { + const char *p, *c; + // telemetry_vbatt_unknown + + if ((previous_cutoff & CURRENTLY_CUTOFF) != 0) { + p = telemetry_vbatt_cutoff; + } + else if ((previous_cutoff & CURRENTLY_VBATT_LOW) != 0) { + p = telemetry_vbatt_low; + } + else if (((previous_cutoff & CURRENTLY_CUTOFF) == 0) && (previous_cutoff & CURRENTLY_VBATT_LOW) == 0){ + p = telemetry_vbatt_normal; + } + else { + p = telemetry_vbatt_unknown; + } + + if ((current_cutoff & CURRENTLY_CUTOFF) != 0) { + c = telemetry_vbatt_cutoff; + } + else if ((current_cutoff & CURRENTLY_VBATT_LOW) != 0) { + c = telemetry_vbatt_low; + } + else if (((current_cutoff & CURRENTLY_CUTOFF) == 0) && (current_cutoff & CURRENTLY_VBATT_LOW) == 0){ + c = telemetry_vbatt_normal; + } + else { + c = telemetry_vbatt_unknown; + } + + main_wait_for_tx_complete(); + + memset(main_own_aprs_msg, 0x00, sizeof(main_own_aprs_msg)); + main_own_aprs_msg_len = sprintf(main_own_aprs_msg, ">[powersave cutoff change][Vbatt: %dV][previous: %d - %s][currently: %d - %s]", battery_voltage, previous_cutoff, p, current_cutoff, c); + ax25_sendVia(&main_ax25, main_own_path, main_own_path_ln, main_own_aprs_msg, main_own_aprs_msg_len); + //while (main_ax25.dcd == 1); + afsk_txStart(&main_afsk); + main_wait_for_tx_complete(); + +} + +void telemetry_send_status_powersave_registers(uint32_t register_last_sleep, uint32_t register_last_wakeup, uint32_t register_counters, uint32_t monitor, uint32_t last_sleep_time) { + main_wait_for_tx_complete(); + + memset(main_own_aprs_msg, 0x00, sizeof(main_own_aprs_msg)); + main_own_aprs_msg_len = sprintf(main_own_aprs_msg, ">[powersave registers][register_last_sleep: 0x%lX][register_last_wakeup: 0x%lX][register_counters: 0x%lX][monitor: 0x%lX][last_sleep_time: 0x%lX]",register_last_sleep, register_last_wakeup, register_counters, monitor, last_sleep_time); + ax25_sendVia(&main_ax25, main_own_path, main_own_path_ln, main_own_aprs_msg, main_own_aprs_msg_len); + //while (main_ax25.dcd == 1); + afsk_txStart(&main_afsk); + main_wait_for_tx_complete(); +} diff --git a/system/src/aprs/wx.c b/system/src/aprs/wx.c index f9ac196..ad67ee4 100644 --- a/system/src/aprs/wx.c +++ b/system/src/aprs/wx.c @@ -46,7 +46,6 @@ void SendWXFrame(uint16_t windspeed, uint16_t windgusts, uint16_t winddirection, if (wind_speed_mph > wind_gusts_mph) { return; - rte_main_reboot_req = 1; } pressure = (unsigned)(cisnienie * 10); diff --git a/system/src/cmsis/stm32l4xx/system_stm32l4xx.c b/system/src/cmsis/stm32l4xx/system_stm32l4xx.c index ace29de..4e887c7 100644 --- a/system/src/cmsis/stm32l4xx/system_stm32l4xx.c +++ b/system/src/cmsis/stm32l4xx/system_stm32l4xx.c @@ -425,6 +425,39 @@ int system_clock_configure_l4(void) return 0; } +void system_clock_start_rtc_l4(void) { + // starting RTC + RCC->BDCR |= RCC_BDCR_RTCEN; + + // enable write access to RTC registers by writing two magic words + RTC->WPR = 0xCA; + RTC->WPR = 0x53; + + // enter the clock set mode + RTC->ISR |= RTC_ISR_INIT; + + // wait for going into clock set mode + while((RTC->ISR & RTC_ISR_INITF) == 0); + + // set date + RTC->DR = 0x0021A820; + + // set time + RTC->TR = 0x00232711; + + // exit RTC set mode + RTC->ISR &= (0xFFFFFFFF ^ RTC_ISR_INIT); + + // disable wakeup interrupt and wakeup interrupt + RTC->CR = 0; + + // wait for wakeup timer to disable + while((RTC->ISR & RTC_ISR_WUTWF) == 0); + + // set the source clock for RTC wakeup as CK_SPRE + RTC->CR |= RTC_CR_WUCKSEL_2; +} + int system_clock_configure_rtc_l4(void) { int retval = 0; @@ -434,6 +467,9 @@ int system_clock_configure_rtc_l4(void) { // check if LSE is working now uint8_t lse_is_working = ((RCC->BDCR & RCC_BDCR_LSERDY) > 0) ? 1 : 0; + // enable access to backup domain + PWR->CR1 |= PWR_CR1_DBP; + // if LSE is not working reinitialize everything if (lse_is_working == 0) { @@ -449,9 +485,6 @@ int system_clock_configure_rtc_l4(void) { // but clear reset flag before RCC->BDCR &= (0xFFFFFFFF ^ RCC_BDCR_BDRST); - // enable access to backup domain - PWR->CR1 |= PWR_CR1_DBP; - // set the clock source for RTC clock to LSE RCC->BDCR |= RCC_BDCR_RTCSEL_0; @@ -463,35 +496,13 @@ int system_clock_configure_rtc_l4(void) { // wait for LSE to start while((RCC->BDCR & RCC_BDCR_LSERDY) == 0); - - // starting RTC - RCC->BDCR |= RCC_BDCR_RTCEN; - - // enable write access to RTC registers by writing two magic words - RTC->WPR = 0xCA; - RTC->WPR = 0x53; - - // enter the clock set mode - RTC->ISR |= RTC_ISR_INIT; - - // wait for going into clock set mode - while((RTC->ISR & RTC_ISR_INITF) == 0); - - // set date - RTC->DR = 0x0021A820; - - // set time - RTC->TR = 0x00232711; - - // exit RTC set mode - RTC->ISR &= (0xFFFFFFFF ^ RTC_ISR_INIT); - - // set the source clock for RTC wakeup as CK_SPRE - RTC->CR |= RTC_CR_WUCKSEL_2; - - } + // starting and configuring the RTC itself + system_clock_start_rtc_l4(); + + // disable access do backup domain + PWR->CR1 &= (0xFFFFFFFF ^ PWR_CR1_DBP); return retval; } @@ -501,6 +512,11 @@ void system_clock_configure_auto_wakeup_l4(uint16_t seconds) { // enable access to backup domain PWR->CR1 |= PWR_CR1_DBP; + // check if RTC is working + if ((RCC->BDCR & RCC_BDCR_RTCEN) == 0) { + system_clock_start_rtc_l4(); + } + // enable write access to RTC registers by writing two magic words RTC->WPR = 0xCA; RTC->WPR = 0x53; @@ -534,6 +550,9 @@ void system_clock_configure_auto_wakeup_l4(uint16_t seconds) { // enable wakeup interrupt NVIC_EnableIRQ(RTC_WKUP_IRQn); + + // disable access do backup domain + PWR->CR1 &= (0xFFFFFFFF ^ PWR_CR1_DBP); } diff --git a/system/src/modbus_rtu/rtu_getters.c b/system/src/modbus_rtu/rtu_getters.c index 45261de..dca5333 100644 --- a/system/src/modbus_rtu/rtu_getters.c +++ b/system/src/modbus_rtu/rtu_getters.c @@ -670,7 +670,7 @@ void rtu_get_raw_values_string(char* out, uint16_t out_buffer_ln, uint8_t* gener f6_value = rte_wx_modbus_rtu_f6.registers_values[0]; - string_ln = snprintf(out, out_buffer_ln, ">F1V %X, F2V %X, F3V %X, F4V %X, F5V %X, F6V %X", + string_ln = snprintf(out, out_buffer_ln, ">[F1V: %X][F2V: %X][F3V: %X][F4V: %X][F5V: %X][F6V: %X]", (int) f1_value, (int) f2_value, (int) f3_value,