#include "packet_tx_handler.h" #include "rte_wx.h" #include "rte_pv.h" #include "rte_main.h" #include "station_config.h" #include "station_config_target_hw.h" #include "./aprs/beacon.h" #include "./aprs/wx.h" #include "./aprs/telemetry.h" #include "./drivers/serial.h" #include "./umb_master/umb_master.h" #include "../include/etc/rtu_configuration.h" #ifdef STM32L471xx #include "aprsis.h" #include "api/api.h" #include "pwr_save.h" #endif #include "main.h" #include "delay.h" #include "io.h" #define _TELEM_DESCR_INTERVAL 150 uint8_t packet_tx_beacon_interval = 0; uint8_t packet_tx_beacon_counter = 0; uint8_t packet_tx_error_status_interval = 2; uint8_t packet_tx_error_status_counter = 0; uint8_t packet_tx_meteo_interval = 0; uint8_t packet_tx_meteo_counter = 2; uint8_t packet_tx_meteo_kiss_interval = 2; uint8_t packet_tx_meteo_kiss_counter = 0; uint8_t packet_tx_telemetry_interval = 10; uint8_t packet_tx_telemetry_counter = 0; uint8_t packet_tx_telemetry_descr_interval = 155; // 155 uint8_t packet_tx_telemetry_descr_counter = 10; uint8_t packet_tx_modbus_raw_values = (uint8_t)(_TELEM_DESCR_INTERVAL - _WX_INTERVAL * (uint8_t)(_TELEM_DESCR_INTERVAL / 38)); uint8_t packet_tx_modbus_status = (uint8_t)(_TELEM_DESCR_INTERVAL - _WX_INTERVAL * (uint8_t)(_TELEM_DESCR_INTERVAL / 5)); uint8_t packet_tx_more_than_one = 0; #ifdef STM32L471xx uint8_t packet_tx_trigger_tcp = 0; #define API_TRIGGER_STATUS (1 << 1) #define API_TRIGGER_METEO (1 << 2) #define APRSIS_TRIGGER_METEO (1 << 3) #define RECONNECT_APRSIS (1 << 7) #endif void packet_tx_send_wx_frame(void) { main_wait_for_tx_complete(); 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); } void packet_tx_configure(uint8_t meteo_interval, uint8_t beacon_interval, config_data_powersave_mode_t powersave) { packet_tx_meteo_interval = meteo_interval; packet_tx_beacon_interval = beacon_interval; // if user selected aggressive powersave mode the meteo counter must be set back to zero // to prevent quirks with waking from sleep mode packet_tx_meteo_counter = 0; } /** * This function is called from the inside of 'packet_rx_handler' to put an extra wait * if more than one packet is sent from the single call to that function. This is required * to protect against jamming own frames when any path is configured. * */ inline void packet_tx_multi_per_call_handler(void) { // if this consecutive frame sent from one call to this function if (packet_tx_more_than_one > 0) { // wait for previous transmission to complete main_wait_for_tx_complete(); // wait for any possible retransmission to kick in delay_fixed(1000); } else { packet_tx_more_than_one = 1; } } // this shall be called in 10 seconds interval void packet_tx_tcp_handler(void) { #ifdef STM32L471xx uint8_t result = 0; aprsis_return_t aprsis_result = APRSIS_UNKNOWN; if ((packet_tx_trigger_tcp & APRSIS_TRIGGER_METEO) != 0) { // TODO: fixme if (aprsis_connected == 0) { aprsis_result = aprsis_connect_and_login_default(0); if (aprsis_result == APRSIS_OK) { // send APRS-IS frame, if APRS-IS is not connected this function will return immediately aprsis_send_wx_frame(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); } } else { // send APRS-IS frame, if APRS-IS is not connected this function will return immediately aprsis_send_wx_frame(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); } // TODO: fixme // clear the bit packet_tx_trigger_tcp ^= APRSIS_TRIGGER_METEO; } else if ((packet_tx_trigger_tcp & API_TRIGGER_STATUS) != 0) { // TODO: fixme // check if APRS-IS is connected if (aprsis_connected != 0) { // disconnect it before call to API - this disconnection has blocking IO aprsis_disconnect(); // remember to reconnect APRSIS after all API comm will be done packet_tx_trigger_tcp |= RECONNECT_APRSIS; } // TODO: fixme // send status (async) api_send_json_status(); // clear the bit packet_tx_trigger_tcp ^= API_TRIGGER_STATUS; } else if ((packet_tx_trigger_tcp & API_TRIGGER_METEO) != 0) { // TODO: fixme // check if APRS-IS is connected if (aprsis_connected != 0) { // disconnect it before call to API - this disconnection has blocking IO aprsis_disconnect(); // remember to reconnect APRSIS after all API comm will be done packet_tx_trigger_tcp |= RECONNECT_APRSIS; } // TODO: fixme api_calculate_mac(); api_send_json_measuremenets(); // clear the bit packet_tx_trigger_tcp ^= API_TRIGGER_METEO; } else if ((packet_tx_trigger_tcp & RECONNECT_APRSIS) != 0) { // TODO: fixme // result = aprsis_connect_and_login_default(1); // // if (result == APRSIS_OK) { packet_tx_trigger_tcp ^= RECONNECT_APRSIS; // } } #endif } // this shall be called in 60 seconds intervals void packet_tx_handler(const config_data_basic_t * const config_basic, const config_data_mode_t * const config_mode) { dallas_qf_t dallas_qf = DALLAS_QF_UNKNOWN; pressure_qf_t pressure_qf = PRESSURE_QF_UNKNOWN; humidity_qf_t humidity_qf = HUMIDITY_QF_UNKNOWN; wind_qf_t wind_qf = WIND_QF_UNKNOWN; uint16_t ln = 0; // set to one if more than one packet will be send from this function at once (like beacon + telemetry) packet_tx_more_than_one = 0; // increase beacon transmit counters packet_tx_beacon_counter++; packet_tx_error_status_counter++; packet_tx_telemetry_counter++; packet_tx_telemetry_descr_counter++; if ((main_config_data_mode->wx & WX_ENABLED) == WX_ENABLED) { // increse these counters only when WX is enabled packet_tx_meteo_counter++; packet_tx_meteo_kiss_counter++; } // check if there is a time to send own beacon if (packet_tx_beacon_counter >= packet_tx_beacon_interval && packet_tx_beacon_interval != 0) { packet_tx_multi_per_call_handler(); beacon_send_own(0); packet_tx_beacon_counter = 0; } // if WX is enabled if ((main_config_data_mode->wx & WX_ENABLED) == WX_ENABLED) { #ifdef STM32L471xx // send wx packet to APRSIS one minute before radio transmission if (packet_tx_meteo_counter == packet_tx_meteo_interval - 1 && packet_tx_meteo_interval != 0) { // TODO: fixme //packet_tx_trigger_tcp |= APRSIS_TRIGGER_METEO; //aprsis_send_wx_frame(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); } #endif // check if there is a time to send meteo packet through RF if (packet_tx_meteo_counter >= packet_tx_meteo_interval && packet_tx_meteo_interval != 0) { // this function is required if more than one RF frame will be send from this function at once // it waits for transmission completion and add some delay to let digipeaters do theris job packet_tx_multi_per_call_handler(); // send WX frame through RF 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 * */ io_ext_watchdog_service(); #ifdef STM32L471xx if (main_config_data_gsm->aprsis_enable == 0 && main_config_data_gsm->api_enable == 1) { // and trigger API wx packet transmission packet_tx_trigger_tcp |= API_TRIGGER_METEO; } else { packet_tx_trigger_tcp = 0; } #endif // check if user want's to send two wx packets one after another if (main_config_data_basic->wx_double_transmit == 1) { rte_main_trigger_wx_packet = 1; } packet_tx_meteo_counter = 0; } if ((main_config_data_mode->wx_modbus & WX_MODBUS_DEBUG) == WX_MODBUS_DEBUG) { // send the status packet with raw values of all requested modbus-RTU registers if (packet_tx_meteo_counter == (packet_tx_meteo_interval - 1) && packet_tx_telemetry_descr_counter >= packet_tx_modbus_raw_values) { packet_tx_multi_per_call_handler(); telemetry_send_status_raw_values_modbus(); } } // there is no sense to include support for Victron VE.Direct in parameteo // which has its own charging controller #ifndef PARAMETEO // check if Victron VE.Direct serial protocol client is enabled and it is // a time to send the status message if (config_mode->victron == 1 && packet_tx_meteo_counter == (packet_tx_meteo_interval - 1) && packet_tx_telemetry_descr_counter >= packet_tx_modbus_raw_values) { packet_tx_multi_per_call_handler(); telemetry_send_status_pv(&rte_pv_average, &rte_pv_last_error, rte_pv_struct.system_state, master_time, rte_pv_messages_count, rte_pv_corrupted_messages_count); } #endif // send wx frame to KISS host once every two minutes if (packet_tx_meteo_kiss_counter >= packet_tx_meteo_kiss_interval && main_kiss_enabled == 1) { // wait if serial port is currently used srl_wait_for_tx_completion(main_kiss_srl_ctx_ptr); SendWXFrameToBuffer(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, main_kiss_srl_ctx_ptr->srl_tx_buf_pointer, main_kiss_srl_ctx_ptr->srl_tx_buf_ln, &ln); srl_start_tx(main_kiss_srl_ctx_ptr, ln); packet_tx_meteo_kiss_counter = 0; } } if (packet_tx_telemetry_counter >= packet_tx_telemetry_interval) { packet_tx_multi_per_call_handler(); // ASSEMBLY QUALITY FACTORS // if there weren't any erros related to the communication with DS12B20 from previous function call if (rte_wx_error_dallas_qf == DALLAS_QF_UNKNOWN) { dallas_qf = rte_wx_current_dallas_qf; // it might be DEGRADATED so we need to copy a value directly // reset current QF to check if there will be at least one successfull readout of temperature rte_wx_current_dallas_qf = DALLAS_QF_UNKNOWN; } // if there were any errors else { // if we had at least one successfull communication with the sensor if (rte_wx_current_dallas_qf == DALLAS_QF_FULL || rte_wx_current_dallas_qf == DALLAS_QF_DEGRADATED) { // set the error reason dallas_qf = DALLAS_QF_DEGRADATED; } // if they wasn't any successfull comm else { // set that it is totally dead and not avaliable dallas_qf = DALLAS_QF_NOT_AVALIABLE; } // and reset the error rte_wx_error_dallas_qf = DALLAS_QF_UNKNOWN; rte_wx_current_dallas_qf = DALLAS_QF_UNKNOWN; } // get quality factors for internal pressure and humidity sensors if (config_mode->wx_ms5611_or_bme == 0) { // pressure sensors quality factors switch (rte_wx_ms5611_qf) { case MS5611_QF_FULL: pressure_qf = PRESSURE_QF_FULL; break; case MS5611_QF_NOT_AVALIABLE: pressure_qf = PRESSURE_QF_NOT_AVALIABLE; break; case MS5611_QF_DEGRADATED: pressure_qf = PRESSURE_QF_DEGRADATED; break; case MS5611_QF_UNKNOWN: pressure_qf = PRESSURE_QF_UNKNOWN; break; } } else if (config_mode->wx_ms5611_or_bme == 1) { //#elif defined(_SENSOR_BME280) // humidity sensors quality factors if (rte_wx_bme280_qf == BME280_QF_UKNOWN) { ; } else { // use BME280 switch (rte_wx_bme280_qf) { case BME280_QF_FULL: case BME280_QF_PRESSURE_DEGRADED: humidity_qf = HUMIDITY_QF_FULL; break; case BME280_QF_UKNOWN: case BME280_QF_NOT_AVAILABLE: humidity_qf = HUMIDITY_QF_NOT_AVALIABLE; break; case BME280_QF_HUMIDITY_DEGRADED: case BME280_QF_GEN_DEGRADED: humidity_qf = HUMIDITY_QF_DEGRADATED; break; } switch (rte_wx_bme280_qf) { case BME280_QF_FULL: case BME280_QF_HUMIDITY_DEGRADED: pressure_qf = PRESSURE_QF_FULL; break; case BME280_QF_UKNOWN: case BME280_QF_NOT_AVAILABLE: pressure_qf = PRESSURE_QF_NOT_AVALIABLE; break; case BME280_QF_PRESSURE_DEGRADED: case BME280_QF_GEN_DEGRADED: pressure_qf = PRESSURE_QF_DEGRADATED; break; } } } else { pressure_qf = PRESSURE_QF_NOT_AVALIABLE; humidity_qf = HUMIDITY_QF_NOT_AVALIABLE; } // wind quality factor if (rte_wx_wind_qf == AN_WIND_QF_UNKNOWN) { ; } else { switch (rte_wx_wind_qf) { case AN_WIND_QF_FULL: wind_qf = WIND_QF_FULL; break; case AN_WIND_QF_DEGRADED_DEBOUNCE: case AN_WIND_QF_DEGRADED_SLEW: case AN_WIND_QF_DEGRADED: wind_qf = WIND_QF_DEGRADATED; break; case AN_WIND_QF_NOT_AVALIABLE: case AN_WIND_QF_UNKNOWN: wind_qf = WIND_QF_NOT_AVALIABLE; break; } 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); } else { #ifdef PARAMETEO // if _DALLAS_AS_TELEM will be enabled the fifth channel will be set to temperature measured by DS12B20 //telemetry_send_values(rx10m, tx10m, digi10m, kiss10m, rte_wx_temperature_dallas_valid, dallas_qf, rte_wx_ms5611_qf, rte_wx_dht_valid.qf, rte_wx_umb_qf); if (config_mode->wx == 1) { // 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_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_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 //telemetry_send_values(rx10m, tx10m, digi10m, kiss10m, rte_wx_temperature_dallas_valid, dallas_qf, rte_wx_ms5611_qf, rte_wx_dht_valid.qf, rte_wx_umb_qf); if (config_mode->wx == 1) { // 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, kiss10m, digidrop10m, rte_wx_temperature_internal_valid, dallas_qf, pressure_qf, humidity_qf, wind_qf, 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, kiss10m, digidrop10m, 0.0f, dallas_qf, pressure_qf, humidity_qf, wind_qf, config_mode); } #endif } packet_tx_telemetry_counter = 0; rx10m = 0, tx10m = 0, digi10m = 0, kiss10m = 0, digidrop10m = 0; } if (packet_tx_telemetry_descr_counter >= packet_tx_telemetry_descr_interval) { #ifdef PARAMETEO telemetry_send_status_powersave_registers(REGISTER_LAST_SLEEP, REGISTER_LAST_WKUP, REGISTER_COUNTERS, REGISTER_MONITOR, REGISTER_LAST_SLTIM); #endif packet_tx_multi_per_call_handler(); if (config_mode->victron == 1) { telemetry_send_chns_description_pv(config_basic); if (rte_pv_battery_voltage == 0) { rte_main_reboot_req = 1; } //telemetry_send_status_pv(&rte_pv_average, &rte_pv_last_error, rte_pv_struct.system_state); } else { telemetry_send_chns_description(config_basic, config_mode); packet_tx_multi_per_call_handler(); //telemetry_send_status(); } telemetry_send_status(); if (config_mode->wx_umb == 1) { umb_clear_error_history(&rte_wx_umb_context); } #ifdef STM32L471xx if (main_config_data_gsm->aprsis_enable == 0 && main_config_data_gsm->api_enable == 1) { // and trigger API wx packet transmission packet_tx_trigger_tcp |= API_TRIGGER_STATUS; } else { packet_tx_trigger_tcp = 0; } if (system_is_rtc_ok() == 0) { rte_main_reboot_req = 1; } #endif packet_tx_telemetry_descr_counter = 0; } } void packet_tx_get_current_counters(packet_tx_counter_values_t * out) { if (out != 0x00) { out->beacon_counter = packet_tx_beacon_counter; out->wx_counter = packet_tx_meteo_counter; out->telemetry_counter = packet_tx_telemetry_counter; out->telemetry_desc_counter = packet_tx_telemetry_descr_counter; out->kiss_counter = packet_tx_meteo_kiss_counter; } } void packet_tx_set_current_counters(packet_tx_counter_values_t * in) { if (in != 0x00) { if (in->beacon_counter != 0) packet_tx_beacon_counter = in->beacon_counter; if (in->wx_counter != 0) packet_tx_meteo_counter = in->wx_counter; if (in->telemetry_counter != 0) packet_tx_telemetry_counter = in->telemetry_counter; if (in->telemetry_desc_counter != 0) packet_tx_telemetry_descr_counter = in->telemetry_desc_counter; if (in->kiss_counter != 0) packet_tx_meteo_kiss_counter = in->kiss_counter; } else { packet_tx_beacon_counter = 0; packet_tx_meteo_counter = 2; packet_tx_telemetry_counter = 0; packet_tx_telemetry_descr_counter = 10; packet_tx_meteo_kiss_counter = 0; } } int16_t packet_tx_get_minutes_to_next_wx(void) { if (packet_tx_meteo_interval != 0) { return packet_tx_meteo_interval - packet_tx_meteo_counter; } else { return -1; } }