![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
work1
Dependencies: mbed MAX44009 mbed-os Si7021
Diff: app/AppMain.cpp
- Revision:
- 1:3656b45f17a8
- Parent:
- 0:d3e390d62607
diff -r d3e390d62607 -r 3656b45f17a8 app/AppMain.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/AppMain.cpp Wed May 27 12:34:37 2020 +0000 @@ -0,0 +1,831 @@ +#include "mbed.h" +#include "mbed_mktime.h" +#include "rtc_api_hal.h" +#include "tools.h" +#include "AppMain.h" +#include "AppLora.h" +#include "Button.h" +#include "LedBlinker.h" +//#include "SensorTag.h" +#include "Sensor.h" +#include "SensorManager.h" +#include "CommandSample.h" +#include "CommandSampleSelector.h" +#include "CommandSampleBoard.h" +#include "AppCommand.h" +//#include "Latch.h" +#include "MemoryDeviceEeprom.h" +#include "Calendar.h" +#include "mbed-trace/mbed_trace.h" +#include "board/SensorBoard.h" +#include "board/PowerManager.h" +#include "rtc.h" +#include "Si7021.h" +#include "ProfileManager.h" +#define MS_PER_SEC 1000 +#define MINUTES_PER_DAY 1440 + +#define JOIN_TRIES 3 + +#define LED_RUN_PERIOD 1000 +#define LED_RUN_COUNT 2 + +// no need to define join failed because +// after all join tries failed the device go in stop mode +// U +// and advertise it +//#define LED_JOIN_FAILED_PERIOD 1000 +//#define LED_JOIN_FAILED_COUNT 3 +// +#define SENSOR_GROUP_MAIN 0x1 +#define SENSOR_GROUP_ALTERNATE 0x2 + +#define LED_JOIN_JOINING_PERIOD 100 +#define JOIN_RETRY_DELAY_MS 3000 + +#define LED_STOP_PERIOD 1000 +#define LED_STOP_COUNT 5 +#define COMMAND_TIMESTAMP_PORT 0x80 + +#define RUN_MODE_STANDARD 0x01 +#define RUN_MODE_TEST 0x02 +#define RUN_MODE_PRODUCTION_TEST 0x03 + +#ifdef MBED_CONF_APP_RUN_MODE +#endif +// TEST +#if MBED_CONF_APP_RUN_MODE == RUN_MODE_STANDARD + #define SAMPLING_PERIOD 5 + #define NUMBER_OF_SAMPLE_BEFORE_SEND 1 + #define NUMBER_OF_TRANSMIT_BEFORE_ALERNATE 1 +#elif MBED_CONF_APP_RUN_MODE == RUN_MODE_PRODUCTION_TEST + #define SKIP_JOIN + #define SAMPLING_PERIOD 1 + #define NUMBER_OF_SAMPLE_BEFORE_SEND 255 + #define NUMBER_OF_TRANSMIT_BEFORE_ALERNATE 1 +#elif MBED_CONF_APP_RUN_MODE == RUN_MODE_TEST + #define SAMPLING_PERIOD 2 + #define NUMBER_OF_SAMPLE_BEFORE_SEND 1 + #define NUMBER_OF_TRANSMIT_BEFORE_ALERNATE 1 +#endif +#define MAX_SEND_DELAY (SAMPLING_PERIOD*60-15) + +#define SOFTWARE_WATCHDOG_TIMEOUT 12 + + +#define CALENDAR_CONFIGURATION_SIZE 1 // to save the start time offset +#define CALENDAR_DAY_COUNT 32 +#define CALENDAR_TIME_SLOT_SIZE 5 // in minutes +#define CALENDAR_TIME_SLOT_PER_DAY ((MINUTES_PER_DAY) / (CALENDAR_TIME_SLOT_SIZE)) + +#define CALENDAR_BIT_COUNT ((CALENDAR_TIME_SLOT_PER_DAY)*(CALENDAR_DAY_COUNT) + \ + CALENDAR_CONFIGURATION_SIZE*8) + +// +7 for ceiling +#define CALENDAR_BYTES_COUNT ((CALENDAR_BIT_COUNT+7)/8) + + +#define LATCH_TRIGGER_VOLTAGE 12000 // 12 volt +#define TRACE_GROUP "sm" + + +using namespace events; +using namespace liboo; + +// TODO Too much code in this modules, it will need some refactoring + + +Sensor *AppMain::_sensor_array[CONFIG_SENSOR_COUNT] = {0}; +SensorManager *AppMain::_sensor_manager = NULL; +EventQueue *AppMain::_queue = NULL; +LedBlinker *AppMain::_led_blinker = NULL; +Button *AppMain::_button = NULL; +//Calendar *AppMain::_calendar = NULL; +//MemoryDeviceEeprom *AppMain::_memory_device_calendar = NULL; +//Latch *AppMain::_latch = NULL; + +I2C i2c(I2C_SDA, I2C_SCL); +SensorData AppMain::_sensor_data; + +AppMain::GLOBAL_STATE AppMain::_global_state; +AppMain::RUN_STATE AppMain::_run_state; + +u32 AppMain::_daily_send_s; +u8 AppMain::_remaining_join_tries; +u8 AppMain::_remaining_sample_before_send; +u8 AppMain::_transmit_count_before_alternate; +u8 AppMain::_send_delay; +u8 AppMain::_history_size; +//bool AppMain::_forced_timestamp; +//bool AppMain::_next_frame_timestamped; + +// buffer for uplink commands +u8 AppMain::_command_payload[COMMAND_PAYLOAD_SIZE]; +char AppMain::_time_buffer[24]; + +u8 AppMain::_software_watchdog_time; +bool AppMain::_software_watchdog_is_enable; + +void AppMain::_software_watchdog_enable(){ + _software_watchdog_reset(); + _software_watchdog_is_enable = true; +} + +void AppMain::_software_watchdog_disable(){ + _software_watchdog_is_enable = false; + +} + +void AppMain::_software_watchdog_reset(){ + _software_watchdog_time = 0; +} + +void AppMain::_software_watchdog_increment(){ + if(!_software_watchdog_is_enable){ + return; + } + ++_software_watchdog_time; + if(_software_watchdog_time >= SOFTWARE_WATCHDOG_TIMEOUT){ + tr_error("watchdog triggered, the system will restart"); + NVIC_SystemReset(); + //mbed_reset(); + } +} + +CommandSample *AppMain::_command_sample_array[COMMAND_COUNT] = { + new CommandSampleCapacitive1(), + new CommandSampleCapacitive2(), + new CommandSampleCapacitive3(), + new CommandSampleCapacitive4(), + new CommandSampleCapacitive5(), + //new CommandSampleTest(), + /* + new CommandSampleGenericTag1(), + new CommandSample1Tag1(), + new CommandSample2Tag1(), + new CommandSample3Tag1(), + new CommandSample4Tag1(), + new CommandSample5Tag1(), + new CommandSample6Tag1(), + new CommandSample7Tag1(), + new CommandSampleGenericAlternativeTag1(), + new CommandSample1AlternativeTag1(), + new CommandSample2AlternativeTag1(), + new CommandSample3AlternativeTag1(), + new CommandSample4AlternativeTag1(), + new CommandSample5AlternativeTag1(), + new CommandSample6AlternativeTag1(), + new CommandSample7AlternativeTag1(), + */ + +}; + +CommandSampleSelector AppMain::_command_selector(_command_sample_array, COMMAND_COUNT, + &_sensor_data); + +void AppMain::set_history_size(u8 history_size){ + if(history_size < 9){ + _history_size = history_size; + tr_info("setting history size to %d", _history_size); + } +} + +void AppMain::_minute_elapsed_callback_handler(void){ + tr_debug("one minute elapsed "); + + Rtc::get_instance()->get_formatted_time(_time_buffer); + //_write_time(_time_buffer); + tr_info("time: %s", _time_buffer); + _software_watchdog_increment(); + _queue->call(callback(_global_event_handler), EVENT_MINUTE_ELAPSED); +} + +void AppMain::_daily_send_callback_handler(void){ + tr_info("daily callback"); + Rtc::get_instance()->get_formatted_time(_time_buffer); + //_write_time(_time_buffer); + tr_debug("time: %s", _time_buffer); + _queue->call(callback(_global_event_handler), EVENT_DAILY_SEND); +} + +void AppMain::_button_callback_handler(Button::ACTION action){ + tr_debug("button handler: "); + switch(action){ + case Button::ACTION_SHORT: + tr_debug("\tshort"); + _queue->call(callback(&AppMain::_global_event_handler), + AppMain::EVENT_SHORT_PUSH); + break; + case Button::ACTION_LONG: + tr_debug("\tshort"); + _queue->call(callback(&AppMain::_global_event_handler), + AppMain::EVENT_LONG_PUSH); + break; + } +} + + +void AppMain::_lora_callback_handler(AppLora::EVENT lora_event){ + switch(lora_event){ + case AppLora::EVENT_CONNECTED: + _global_event_handler(AppMain::EVENT_JOIN_SUCCESS); + break; + case AppLora::EVENT_DISCONNECTED: + _global_event_handler(AppMain::EVENT_DISCONNECTED); + break; + case AppLora::EVENT_CONNECTION_ERROR: + _global_event_handler(AppMain::EVENT_CONNECTION_ERROR); + break; + case AppLora::EVENT_JOIN_FAILURE: + _global_event_handler(AppMain::EVENT_JOIN_FAILED); + break; + case AppLora::EVENT_TX_DONE: + _global_event_handler(AppMain::EVENT_TX_DONE); + tr_info("tx done"); + break; + case AppLora::EVENT_TX_ERROR: + _global_event_handler(AppMain::EVENT_TX_ERROR); + tr_err("tx error"); + break; + case AppLora::EVENT_RX_ERROR: + _global_event_handler(AppMain::EVENT_RX_ERROR); + tr_err("rx error"); + break; + default: + // nothing to do for now + break; + } +} +void AppMain::_global_event_handler_from_interrupt(AppMain::EVENT event){ + _queue->call(callback(&AppMain::_global_event_handler), event); +} + +void AppMain::_enter_global_state_stop(void){ + tr_info("enter stop mode"); + _software_watchdog_disable(); + _global_state = AppMain::GLOBAL_STATE_STOP; + _remaining_join_tries = JOIN_TRIES; + + // sensor manager allocated in create_sensors + Rtc *rtc = Rtc::get_instance(); + // disable minutes callback + rtc->disable_alarm(Rtc::ALARM_A); + // disable daily callback + rtc->disable_alarm(Rtc::ALARM_B); + //_calendar->disable(); + PowerManager *pm= PowerManager::get_instance(); + pm->sleep_mode(); + + _led_blinker->blink(LED_STOP_PERIOD, LED_STOP_COUNT); + + + // unlock latch +} + +void AppMain::_enter_global_state_disconnecting(void){ + tr_info("enter disconnecting mode"); + _global_state = AppMain::GLOBAL_STATE_DISCONNECTING; + _queue->call(AppLora::disconnect); +} + +void AppMain::_enter_global_state_join(void){ + tr_info("enter join mode"); + _global_state = AppMain::GLOBAL_STATE_JOIN; + _queue->call(AppLora::connect); + + // this call can potentially make the rx window drifting + // because the led is blinked through I2C, this takes time + //_led_blinker->blink_from_interrupt(LED_JOIN_JOINING_PERIOD, + //LedBlinker::COUNT_INFINITE); +} +#if 0 +void AppMain::_write_time(char* buffer){ + time_t time; + tm time_info; + Rtc &rtc = Rtc::get_instance(); + time = rtc.get_time(); + // epoch timestamp the 1 January 2018 0h00 + time += 1514764800; + _rtc_localtime(time, &time_info, RTC_4_YEAR_LEAP_YEAR_SUPPORT); + sprintf(buffer, "%d-%d-%d %d:%d:%d", time_info.tm_mday, + time_info.tm_mon+1, time_info.tm_year+1900, time_info.tm_hour, + time_info.tm_min, time_info.tm_sec); +} +#endif + +void AppMain::_enter_global_state_run(void){ + tr_info("enter run mode"); + _software_watchdog_enable(); + _global_state = AppMain::GLOBAL_STATE_RUN; + _transmit_count_before_alternate = 1; // first packet use alternate + + // random number are recomputed at each restart + AppMain::_compute_random_number(); + + Rtc *rtc = Rtc::get_instance(); + time_t time = _daily_send_s; + tm time_info; + _rtc_localtime(time, &time_info, RTC_4_YEAR_LEAP_YEAR_SUPPORT); + + // schedule sampling, sending and calendar check + rtc->set_alarm(Rtc::ALARM_A, Rtc::ALARM_FIELD_UNUSED, + Rtc::ALARM_FIELD_UNUSED, 0x00); + + + tr_info("setting daily send to %d:%d:%d", time_info.tm_hour, + time_info.tm_min, time_info.tm_sec); + //rtc.set_alarm(Rtc::ALARM_B, Rtc::ALARM_FIELD_UNUSED, + //Rtc::ALARM_FIELD_UNUSED, time_info.tm_sec); + rtc->set_alarm(Rtc::ALARM_B, time_info.tm_hour, + time_info.tm_min, time_info.tm_sec); + + _led_blinker->blink(LED_RUN_PERIOD, LED_RUN_COUNT); + + // when start run mode check calendar + // to check the latch is in correct state +#if 0 + tr_debug("enable calendar"); + //_calendar->enable(); + tr_debug("checking calendar"); + //_calendar->check(rtc->get_time()); + tr_debug("check calendar done"); + //wait(5); +#endif + //_forced_timestamp = true; + + // erase all old samples + _sensor_data.reset(); + // send timestamped packet until answer + //_forced_timestamp = true; + + // send a first message at boot + // It enable to get the next packet size +#if 0 + if(_send_empty_timestamped() < 0){ + _run_state = AppMain::RUN_STATE_WAIT_SEND_COMPLETE; + } + else{ + _run_state = AppMain::RUN_STATE_READY; + } +#endif + +} + +void AppMain::_global_event_handler(AppMain::EVENT event){ + tr_debug("global_event_handler"); + tr_debug("\tglobal_state: %d", _global_state); + tr_debug("\tevent : %d", event); + switch(_global_state){ + case GLOBAL_STATE_STOP: + if(event == AppMain::EVENT_SHORT_PUSH){ + _enter_global_state_join(); + } + break; + case GLOBAL_STATE_JOIN: + if(event == AppMain::EVENT_JOIN_SUCCESS){ + _led_blinker->stop(); + _enter_global_state_run(); + } + if(event == AppMain::EVENT_CONNECTION_ERROR || + event == AppMain::EVENT_TX_ERROR || + event == AppMain::EVENT_JOIN_FAILED){ + --_remaining_join_tries; + if(_remaining_join_tries == 0){ + tr_err("join failed after %d tries", JOIN_TRIES); + _enter_global_state_stop(); + } + else{ + //retry join + tr_warn("join failed, retry join in %dms", JOIN_RETRY_DELAY_MS); + _led_blinker->stop(); + //wait_ms(JOIN_RETRY_DELAY_MS); + //ThisThread::sleep_for(JOIN_RETRY_DELAY_MS); + wait_us(JOIN_RETRY_DELAY_MS*1000); + _enter_global_state_join(); + } + } + + break; + case GLOBAL_STATE_RUN: + if(event == AppMain::EVENT_SHORT_PUSH){ + _send_empty(); + //_write_time(_time_buffer); + Rtc::get_instance()->get_formatted_time(_time_buffer); + tr_info("time: %s", _time_buffer); + //Rtc &rtc = Rtc::get_instance(); + //rtc.set_time(9537861); + + } + else if(event == AppMain::EVENT_LONG_PUSH){ + _enter_global_state_disconnecting(); + } + else{ + _run_event_handler(event); + } + break; + case GLOBAL_STATE_DISCONNECTING: + if(event == AppMain::EVENT_DISCONNECTED){ + _enter_global_state_stop(); + } + } +} + + +void AppMain::_run_event_handler(AppMain::EVENT event){ + switch(_run_state){ + case RUN_STATE_READY: + if(event == AppMain::EVENT_MINUTE_ELAPSED){ + Rtc *rtc = Rtc::get_instance(); + time_t time = rtc->get_time(); + tm time_info; + _rtc_localtime(time, &time_info, RTC_4_YEAR_LEAP_YEAR_SUPPORT); + + if((time_info.tm_min % SAMPLING_PERIOD) == 0) // if minute is pair + { + bool is_hourly_send = false; + // forbid latch operation to avoid wrong measure or degraded RF + // this cannot protect against a latch which is already charging + // it will forbid only trigger latch + // TODO lock latch + _sensor_manager->sample_all(); + --_remaining_sample_before_send; + + tr_debug("remaining sampling before transmit: %d", + _remaining_sample_before_send); + if(_remaining_sample_before_send == 0){ + /* + static u8 sample_id = 0; + ++sample_id; + */ + _remaining_sample_before_send = NUMBER_OF_SAMPLE_BEFORE_SEND; + --_transmit_count_before_alternate; + if(_transmit_count_before_alternate == 0){ + _transmit_count_before_alternate = NUMBER_OF_TRANSMIT_BEFORE_ALERNATE; + is_hourly_send = 1; + } + + _sensor_manager->record_samples(); + if(!_sensor_manager->are_all_sensor_disabled()){ + tr_info(" Scheduling transmit in %d secondes", _send_delay); + //_write_time(_time_buffer); + Rtc::get_instance()->get_formatted_time(_time_buffer); + tr_debug("time: %s", _time_buffer); + _queue->call_in(_send_delay*1000, + callback(_scheduled_send), is_hourly_send); + } + } + else{ + // [TODO]unlock latch + } + //check calendar every minnutes +#if 0 + Rtc *rtc = Rtc::get_instance(); + u32 time = rtc->get_time(); + _calendar->check(time); +#endif + } + +#if MBED_CONF_APP_RUN_MODE == RUN_MODE_PRODUCTION_TEST + tr_info("turn on pump"); + // toggle latch 10s + PowerManager *pm= PowerManager::get_instance(); + pm->lock_and_turn_on_motor(); + wait_us(10*1000*1000); + pm->unlock_and_turn_off_motor(); + tr_info("turn off pump"); +#endif + } + if(event == AppMain::EVENT_DAILY_SEND){ + // at least one packet is timestamped every days + //_next_frame_timestamped = true; + if(_sensor_manager->are_all_sensor_disabled()){ + if(_send_empty() < 0){ + _run_state = RUN_STATE_WAIT_SEND_COMPLETE; + } + } + } + break; + case RUN_STATE_WAIT_SEND_COMPLETE: + // other event like daily send or event minutes elapsed + // doesnt need to be process together because + // theses event are generated in two different mode. + // One is when sensor are enable the other is when all sensor are disabled + if( event == AppMain::EVENT_TX_DONE || + event == AppMain::EVENT_RX_ERROR || + event == AppMain::EVENT_TX_ERROR){ + // TODO unlock latch + _run_state = AppMain::RUN_STATE_READY; + } + break; + } +} + +void AppMain::_compute_random_number(void){ + u32 rand; + //value between 0 and 4 which is the number of samples sinces the last + //send + rand = AppLora::get_random(); + _remaining_sample_before_send = (rand % NUMBER_OF_SAMPLE_BEFORE_SEND) + 1; + //_remaining_sample_before_send = 1; + tr_info("random: Transmit in %d sample", _remaining_sample_before_send); + + // delay between the 5th sample and the send action + // value between 0 and 90 seconds. The actual range could be 0-120 seconds + // but in order to send the data before the next sample 30 secondes are removed. + rand = AppLora::get_random(); + _send_delay = rand % MAX_SEND_DELAY; + tr_info("random: Send delay %ds", _send_delay); + + // hours in seconds for the dayli sent + // this sending is done only when all sensors are deactivated + rand = AppLora::get_random(); + _daily_send_s = rand % 86400; // 86400 number of seconds in one day + tr_info("random: Daily send %lus", _daily_send_s); + +} + +// this function is called only once at start +void AppMain::initialize(EventQueue *queue){ + tr_info("Main initialization"); + _queue = queue; + + ProfileManager* profile_manager = ProfileManager::get_instance(); + + //CommandSample::set_endianness(CommandSample::EndiannessBig); + + AppLora::set_lora_callback(_lora_callback_handler); + //wait(0.05); + wait_us(50000); + + // sensor manager allocated in create_sensors + Rtc *rtc = Rtc::get_instance(); + rtc->set_alarm_callback(Rtc::ALARM_B, _daily_send_callback_handler); + rtc->set_alarm_callback(Rtc::ALARM_A, _minute_elapsed_callback_handler); + + // set default time + // set default time to delivery day: 2018/09/12 00:00 + rtc->set_rtc_time(22032000); + + //pcal_i2c.frequency(100000); + //_gpio_exp = new PCAL6416(pcal_i2c, 0x42); + _led_blinker = new LedBlinker(AppMain::_queue, LED1); + _button = new Button(_led_blinker, USER_BUTTON); + + _history_size = 1; + _create_sensors(); + _button->set_action_callback(_button_callback_handler); + // sensor manager allocated in create_sensors + //_memory_device_calendar = new MemoryDeviceEeprom(0, CALENDAR_BYTES_COUNT); + //_latch = new Latch(LATCH_TRIGGER_VOLTAGE, _gpio_exp); +#if 0 + _calendar = new Calendar(_memory_device_calendar, + CALENDAR_DAY_COUNT, + CALENDAR_TIME_SLOT_PER_DAY, + _trigger_latch); +#endif + AppCommand::configure(_sensor_manager, NULL /* _calendar */); + + // short wait to charge all capacitor before triggering latch + //wait_ms(20); + wait_us(20000); + //_enter_global_state_stop(); +#ifdef SKIP_JOIN + _enter_global_state_run(); +#else + _enter_global_state_join(); +#endif + +} + +void AppMain::_trigger_latch(bool sens){ + if(sens){ + tr_debug("trigger latch forward"); + //_latch->trigger(Latch::TRIGGER_SENS_FORWARD); + } + else{ + tr_debug("trigger latch reverse"); + //_latch->trigger(Latch::TRIGGER_SENS_REVERSE); + } +} + +void AppMain::_create_sensors(void){ + + PowerManager *pm= PowerManager::get_instance(); + pm->run_mode(); + + //TPS62740 *tps = TPS62740::get_instance(); + //tps->set_voltage(TPS62740::V3_0); + + tr_info("initialize sensors"); + tr_debug("initialize battery"); + _sensor_array[0] = new SensorBoardBattery(SENSOR_ID_BOARD_BAT, SENSOR_GROUP_MAIN); + + tr_debug("initialize SI7021"); + Si7021 *si = new Si7021(I2C_SDA, I2C_SCL); + bool ping_success = si->check(); + if(ping_success){ + tr_debug("SI7021 detected"); + } + else{ + tr_warn("couldn't communicate with SI7021"); + } + + _sensor_array[1] = new SensorBoardTempRH(SENSOR_ID_BOARD_TEMPERATURE, + SENSOR_GROUP_MAIN, si); + _sensor_array[2] = new SensorBoardTempRH(SENSOR_ID_BOARD_HUMIDITY, + SENSOR_GROUP_MAIN, si); + + tr_debug("initialize MAX44009"); + MAX44009 *max44009 = new MAX44009(&i2c, 0x94); + _sensor_array[3] = new SensorBoardIllumination(SENSOR_ID_BOARD_ILLUMINATION, + SENSOR_GROUP_MAIN, max44009); + tr_debug("initialize sensor board 1"); + _sensor_array[4] = new SensorBoardCapacitive(SENSOR_ID_BOARD_CAPACITIVE_0, SENSOR_GROUP_MAIN); + tr_debug("initialize sensor board 2"); + _sensor_array[5] = new SensorBoardCapacitive(SENSOR_ID_BOARD_CAPACITIVE_1, SENSOR_GROUP_MAIN); + tr_debug("initialize sensor board 3"); + _sensor_array[6] = new SensorBoardCapacitive(SENSOR_ID_BOARD_CAPACITIVE_2, SENSOR_GROUP_MAIN); + tr_debug("initialize sensor board 4"); + _sensor_array[7] = new SensorBoardCapacitive(SENSOR_ID_BOARD_CAPACITIVE_3, SENSOR_GROUP_MAIN); + + //_sensor_array[11] = new SensorImpulse(SENSOR_ID_IMPULSE); + + //_sensor_array[11] = new SensorImpulse(SENSOR_ID_IMPULSE); + //_sensor_data = new SensorData(); + //_sensor_manager = new SensorManager(_sensor_data, _sensor_array, 11); + tr_debug("initialize sensor manager"); + _sensor_manager = SensorManager::get_instance(); + _sensor_manager->initialize(&_sensor_data, _sensor_array, CONFIG_SENSOR_COUNT); + tr_info("initialization done"); +} + +void AppMain::incoming_lora_message_callback(void){ + u8 app_port; + u8 data[256]; + s16 received_size; + u16 data_size = 256; + + //_forced_timestamp = false; + received_size = AppLora::get_rx_message(data, data_size, app_port); + if(received_size >= 0){ + AppCommand::process_command(app_port, data, received_size); + } + // else it an error +} + +void AppMain::_scheduled_send(bool is_hourly_send){ + tr_info("proceed scheduled send"); + Rtc::get_instance()->get_formatted_time(_time_buffer); + + //_write_time(_time_buffer); + tr_debug("time: %s", _time_buffer); + if(_send_samples(is_hourly_send) >= 0){ + _run_state = RUN_STATE_WAIT_SEND_COMPLETE; + } +} + +s8 AppMain::_send_samples(bool is_hourly_send){ + CommandSample *selected_command; + //command_field_t field; + u32 timestamp; + s16 retcode; + u8 max_lora_payload; + u8 command_payload_size = 0; + u8 command_port = 0; + //u8 history_size = _history_size; + u8 history_size; + u8 command_group = SENSOR_GROUP_MAIN; + //bool _add_timestamp = false; + + // select if the frame must be timestamped + // not generic + //if(_next_frame_timestamped || _forced_timestamp){ + //_add_timestamp = true; + //} + + // not generic + if(is_hourly_send){ + // do nothing for now + //history_size = _history_size; + history_size = 1; + command_group = SENSOR_GROUP_MAIN; + } + else { + history_size = _history_size; + } + // get mask, it tells what are the sensor used which have set values + // in memory for a given history size + + //_command_selector.compute_requiered_field(&_sensor_data, history_size, &field); + +#if 0 + if(!is_hourly_send){ + //these value are only set one time by hours + field.battery = 0; + field.board_temperature = 0; + field.board_humidity = 0; + } +#endif + /* + else{ + // it is the hourly send + // do not use history + history_size = 1; + } + */ + + //tr_debug("requested fields: 0x%02u", field.bits); + // the the mask is used to select the smallest command + //selected_command = _command_selector.get_command_sample(&_sensor_data, &field, history_size); + selected_command = _command_selector.get_command_sample(history_size, command_group); + + + bool is_pump_on = AppCommand::get_pump_state(); + //tr_debug("requested fields: 0x%02u", field.bits); + if(selected_command == NULL){ + tr_err("no matching command"); + return -1; + } + else{ + tr_info("command %d selected", selected_command->get_command_id(false, + is_pump_on/*_calendar->is_enable()*/ )); + } + // get maxlora payload + max_lora_payload = AppLora::get_next_transmission_max_size(); + tr_info("LoRa max packet %d", max_lora_payload); + + // Decrement the size of history while the command is bigger than + // the max lora payload size (for the current bandwith and sf) + + // not completly optmised, changing the history size can also + // change the command used. Here we keep using the same command + tr_info("expected history size %d", history_size); + while(selected_command->get_command_size(history_size, false) > + max_lora_payload){ + --history_size; + + // not sure it can happen but just in case + // Doesnt try to send packet for history size of 0, this doesnt make sense + tr_warn("history size too, using : %d instead", history_size); + if(history_size == 0){ + tr_err("history size: 0\r\n"); + return -1; + } + } + tr_info("selected history size %d", history_size); + // + // command id is different if timestamp is used + command_port = selected_command->get_command_id(false, + is_pump_on /* _calendar->is_enable() */); + + tr_debug("using command port: %d", command_port); + // get the timestamp at the last moment + Rtc *rtc = Rtc::get_instance(); + timestamp = rtc->get_time(); + + command_payload_size = selected_command->generate_command( + &_sensor_data, history_size, + false, timestamp, + _command_payload, COMMAND_PAYLOAD_SIZE); + + tr_info("payload size : %d", command_payload_size); + if(_global_state == GLOBAL_STATE_RUN){ + tr_info("sending samples"); + // acknowledged -> false + retcode = AppLora::send_message(_command_payload, + command_payload_size, command_port, false); + if(retcode < 0){ + tr_err("send error %d\r\n", retcode); + return -1; + } + else{ + _software_watchdog_reset(); + //_next_frame_timestamped = false; + } + } + return 0; +} + +s8 AppMain::_send_empty(void){ + u16 command_size; + u32 time; + + Rtc *rtc = Rtc::get_instance(); + time = rtc->get_time(); + command_size = CommandSample::generate_raw_command(true, time, NULL, 0, + _command_payload, COMMAND_PAYLOAD_SIZE); + if(_global_state == GLOBAL_STATE_RUN){ + s16 retcode; + tr_info("sending empty frame"); + // acknowledged -> false + retcode = AppLora::send_message(_command_payload, command_size, + COMMAND_TIMESTAMP_PORT, false); + if(retcode < 0){ + tr_err("send error %d\r\n", retcode); + return -1; + } + } + return 0; +} + +