Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed MAX44009 mbed-os Si7021
Diff: AppMain.cpp
- Revision:
- 0:d3e390d62607
diff -r 000000000000 -r d3e390d62607 AppMain.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/AppMain.cpp Wed May 27 11:37:29 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;
+}
+
+