Timer creation sporadically fails

Hi there,

I use the following code (snippet) after setting up the connection to a MetaMotionR and after initializing it to then activate some modules and to setup some processors and timers:

mbl_mw_metawearboard_tear_down(m_metawear_board);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
mbl_mw_settings_set_connection_parameters(m_metawear_board, 7.5, 7.5, 0, 6000);

switch_dc_promise = std::promise<bool>();
auto switch_future = switch_dc_promise.get_future();
auto switch_signal = mbl_mw_switch_get_state_data_signal(m_metawear_board);
mbl_mw_dataprocessor_passthrough_create(switch_signal, MBL_MW_PASSTHROUGH_MODE_COUNT, 0, nullptr,
                                    C_MWC::c_switch_dc_recorded);
switch_future.wait();

switch_processor_promise = std::promise<bool>();
auto switch_processor_future = switch_processor_promise.get_future();
if (switch_dc_processor != nullptr) {
    mbl_mw_event_record_commands(switch_dc_processor);
    mbl_mw_led_stop_and_clear(m_metawear_board);
    mbl_mw_event_end_record(switch_dc_processor, nullptr,
                            C_MWC::c_switch_processor_recorded);
    switch_processor_future.get();
}

timer_promise = std::promise<int>();
auto timer_future = timer_promise.get_future();
auto batt_signal = mbl_mw_settings_get_battery_state_data_signal(m_metawear_board);
mbl_mw_datasignal_subscribe(batt_signal, nullptr, C_MWC::c_handle_battery);
mbl_mw_timer_create_indefinite(m_metawear_board, 500, 0, nullptr, C_MWC::c_timer_created);
timer_future.get();
mbl_mw_datasignal_read(batt_signal);

disconnect_promise = std::promise<bool>();
auto disconnect_future = disconnect_promise.get_future();
// Setup disconnect event to stop fusion and set led to red
MblMwEvent *dc_event = mbl_mw_settings_get_disconnect_event(m_metawear_board);
mbl_mw_event_record_commands(dc_event);
mbl_mw_sensor_fusion_stop(m_metawear_board);
mbl_mw_sensor_fusion_clear_enabled_mask(m_metawear_board);
mbl_mw_gyro_bmi160_stop(m_metawear_board);
mbl_mw_gyro_bmi160_disable_rotation_sampling(m_metawear_board);
mbl_mw_acc_stop(m_metawear_board);
mbl_mw_acc_disable_acceleration_sampling(m_metawear_board);
auto timer = mbl_mw_timer_lookup_id(m_metawear_board, m_battery_id);
if (timer != nullptr) {
    mbl_mw_timer_stop(timer);
    mbl_mw_timer_remove(timer);
}
if (switch_dc_processor != nullptr) mbl_mw_dataprocessor_passthrough_set_count(switch_dc_processor, 1);

mbl_mw_event_end_record(dc_event, nullptr, C_MWC::c_disconnect_recorded);
disconnect_future.get();

I use the promises to synchronize the asynchronous callbacks and the callbacks are handled in a friend class with static methods and a static reference to the class setting everything up, so that I have access to the members from there.

The callbacks of the snippet above are the following:

void C_MWC::c_switch_dc_recorded(void *context, MblMwDataProcessor *processor) {
    this_->switch_dc_recorded(processor);
}

void switch_dc_recorded(MblMwDataProcessor *pProcessor) {
    switch_dc_processor = pProcessor;
    switch_dc_promise.set_value(true);
}

void C_MWC::c_switch_processor_recorded(void *context, MblMwEvent *event, int32_t status) {
    this_->switch_processor_recorded(event, status);
}

void switch_processor_recorded(MblMwEvent *pEvent, int32_t status) {
    switch_processor_promise.set_value(status == 0);
}

void C_MWC::c_timer_created(void *cointext, MblMwTimer *timer) {
    this_->timer_created(timer);
}

void timer_created(MblMwTimer *pTimer) {
    if (pTimer != nullptr) {
        m_battery_timer = pTimer;
        m_battery_id = mbl_mw_timer_get_id(m_battery_timer);
        auto battery_signal = mbl_mw_settings_get_battery_state_data_signal(m_metawear_board);
        mbl_mw_event_record_commands((MblMwEvent *) pTimer);
        mbl_mw_datasignal_read(battery_signal);
        mbl_mw_event_end_record((MblMwEvent *) pTimer, nullptr, C_MWC::c_cmds_recorded);
    } else {
        m_logger.debug("Received a nullptr in timer_created");
        cmds_recorded(nullptr, TIMER_CREATION_ERROR_NULLPTR);
    }
}

void C_MWC::c_cmds_recorded(void *context, MblMwEvent *evt, int32_t status) {
    this_->cmds_recorded(evt, status);
}

void cmds_recorded(MblMwEvent *pEvent, int32_t status) {
    switch (status) {
        case MBL_MW_STATUS_OK:
            m_logger.debug("battery timer setup done!");
            mbl_mw_timer_start((MblMwTimer *) pEvent);
            break;
        case MBL_MW_STATUS_ERROR_TIMEOUT:
            m_logger.debug("timed out on recording battery reading command!");
            break;
        case TIMER_CREATION_ERROR_NULLPTR:
            m_logger.debug("was not able to get a valid timer pointer!");
            break;
        default:
            m_logger.debug("was not able to setup battery timer [UNKNOWN]");
            break;
    }

    timer_promise.set_value(status);
}

void C_MWC::c_disconnect_recorded(void *context, MblMwEvent *event, int32_t status) {
    this_->disconnect_recorded(event, status);
}

void disconnect_recorded(MblMwEvent *pEvent, int32_t status) {
    if (status == MBL_MW_STATUS_OK) {
        disconnect_promise.set_value(true);
    } else {
        if (status == MBL_MW_STATUS_ERROR_TIMEOUT) m_logger.debug("[TIMEOUT]");
        disconnect_promise.set_value(false);
    }
}

On the first connection to a sensor this is working, all the events and processors are created successfully. Unfortunately, after some successful connections, I am unable to get a valid timer when executing

mbl_mw_timer_create_indefinite(m_metawear_board, 500, 0, nullptr, C_MWC::c_timer_created);

In the callback I only get a nullptr as the MblMwTimer* parameter.

Currently I can only resolve this by resetting the sensor. Afterwards I get a valid timer on the first connection, and start getting nullptrs after some connections (sometimes, but not always, as soon as the second one).

All the other processors are still created successfully, and I can also use sensor fusion, the switch module, the vibration motor and the LED. Only the timer I create to periodically check the current battery is not working.

Can you see some any mistakes I made when setting this up? Is there anything I can do to mitigate that problem?

Thank you!
Marcus

Comments

  • I forgot to mention that I remove the created timers and processors before disconnecting, e.g. to remove the mentioned battery timer I use:

    if (m_battery_timer != nullptr) {
        m_logger.debug("removing battery timer");
        auto timer = mbl_mw_timer_lookup_id(m_metawear_board, m_battery_id);
        if (timer != nullptr) {
            mbl_mw_timer_stop(timer);
            mbl_mw_timer_remove(timer);
        }
        m_battery_timer = nullptr;
        m_battery_id = 0;
    } else {
        m_logger.debug("no battery timer to remove");
    }
    mbl_mw_datasignal_unsubscribe(mbl_mw_settings_get_battery_state_data_signal(m_metawear_board));
    
  • What is the id of the timers that are created prior to receiving a nullptr (mbl_mw_timer_get_id)?
    Can you confirm that the remove command is being received by the device before the script terminates?

  • HI, sorry for not replying sooner.

    I read out the IDs I receive before getting a nullptr and got the following:

    14.08.2018 12:46:55.349 [debug] MetaWearDevice Setting up modules
    14.08.2018 12:46:55.349 [debug] MetaWearDevice Enabling battery
    14.08.2018 12:46:55.449 [debug] MetaWearDevice Created the battery timer with timer ID: 0
    
    14.08.2018 12:48:02.400 [debug] MetaWearDevice Setting up modules
    14.08.2018 12:48:02.400 [debug] MetaWearDevice Enabling battery
    14.08.2018 12:48:02.500 [debug] MetaWearDevice Created the battery timer with timer ID: 1
    
    14.08.2018 12:48:25.000 [debug] MetaWearDevice Enabling battery
    14.08.2018 12:48:25.101 [debug] MetaWearDevice Created the battery timer with timer ID: 2
    
    14.08.2018 12:49:24.551 [debug] MetaWearDevice Enabling battery
    14.08.2018 12:49:24.701 [debug] MetaWearDevice Created the battery timer with timer ID: 3
    

    As the IDs increased every time and as you asked me to confirm, that the timer is actually removed before the sensor is disconnected, I had a look again at the code, where the timer is removed.

    As I posted before, the code was:

        auto timer = mbl_mw_timer_lookup_id(m_metawear_board, m_battery_id);
        if (timer != nullptr) {
            mbl_mw_timer_stop(timer);
            mbl_mw_timer_remove(timer);
        }
        m_battery_timer = nullptr;
    

    I included some additional logging and received the following outputs:

    14.08.2018 12:52:36.604 [debug] MetaWearDevice Enabling battery
    14.08.2018 12:52:36.704 [debug] MetaWearDevice Created the battery timer with timer ID: 4
    ...
    got request: 'disconnect'
    14.08.2018 12:52:39.591 [debug] MetaWearDevice removing battery timer for battery_id 4
    14.08.2018 12:52:39.591 [debug] MetaWearDevice Looking up the battery id resulted in a nullptr. Can't remove timer!
    
    14.08.2018 12:54:44.455 [debug] MetaWearDevice Created the battery timer with timer ID: 5
    ...
    got request: 'disconnect'
    14.08.2018 12:54:46.889 [debug] MetaWearDevice removing battery timer for battery_id 5
    14.08.2018 12:54:46.889 [debug] MetaWearDevice Looking up the battery id resulted in a nullptr. Can't remove timer!
    

    So the problem seems to be, that I receive a nullptr on mbl_mw_timer_lookup_id and am therefore not removing the timer. As a workaround I will not request it before removing but instead remember it after creation, but what might be the problem here?

  • I think I found the problem.

    I am setting up a disconnect handler on board, that disables the modules I activate, as well as trying to remove any timers and processors.

    Currently the following code is executed:

    disconnect_promise = std::promise<bool>();
    auto disconnect_future = disconnect_promise.get_future();
    MblMwEvent *dc_event = mbl_mw_settings_get_disconnect_event(m_metawear_board);
    mbl_mw_event_record_commands(dc_event);
    // ...
    // Disabling of some modules
    // ...
    auto timer = mbl_mw_timer_lookup_id(m_metawear_board, m_battery_id);
    if (timer != nullptr) {
        mbl_mw_timer_stop(timer);
        mbl_mw_timer_remove(timer);
    }
    if (switch_dc_processor != nullptr) mbl_mw_dataprocessor_passthrough_set_count(switch_dc_processor, 1);
    mbl_mw_event_end_record(dc_event, nullptr, C_MWC::c_disconnect_recorded);
    disconnect_future.get();
    

    If I remove the line with mbl_mw_timer_remove(timer); from this code block, leaving only the mbl_mw_timer_stop(timer); everything then works as expected. I receive a valid timer during the actual disconnect using mbl_mw_timer_lookup_id and on the next connection I receive the same timer ID, which I guess means, that the timer was actually removed.

    I therefore assume, that by calling mbl_mw_timer_remove during the mbl_mw_event_record_commands I somehow invalidate the timer? Am I actually removing it from the board during the recording?

    Unfortunately, the timer is now of course no longer removed on an unexpected disconnect in the disconnect handler. So after a connection loss I get a timer with a higher ID.

    Is there some way to remove timers (and processors I guess) during the registering of a disconnect handler without making them invalid?

  • I am also wondering about the effect of the mbl_mw_metawearboard_tear_down I am doing directly after connecting to the board.

    In the API it is described as

    Removes all data processors and timers from the MetaWear board. 
    

    But it seems not to remove any stale timers. Do I have to use it differently?

  • mbl_mw_metawearboard_tear_down only removes timers it is aware of; it is meant to be called at the end of a script.

    You already have the timer pointer so just use m_battery_timer directly instead of looking it up again by id.

    Given that you're running so much code to stop everything, just reset the board on disconnect.

Sign In or Register to comment.