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:
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:
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:
I included some additional logging and received the following outputs:
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:
If I remove the line with
mbl_mw_timer_remove(timer);
from this code block, leaving only thembl_mw_timer_stop(timer);
everything then works as expected. I receive a valid timer during the actual disconnect usingmbl_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 thembl_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
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.
Hi,
sorry to reopen this stale thread, but I have question more or less regarding the topics already discussed here.
Currently we chose to follow your suggestion and to just reset the board on disconnect to remove all timers and processors should we loose the connection.
We do this as we still face the problem that removing timers or dataprocessors during the disconnect event recording results in them being removed instantly, not after a disconnect, which makes this approach unusable.
But we are currently thinking about implementing a charging indicator for disconnected boards that would require a registered handler for the charge_status_data_signal that survives a disconnect.
Is it possible to setup something like this?
In detail we would need the following: