Re: GCS control failsafe settings
« Reply #15 on: January 12, 2020, 07:16:55 pm »
For simplicity, I would probably poll on a timer.

I think I recall that receiver code effectively is polled on a timer, so you could even just put a counter in it.  I think I recall seeing a counter there already.

Re: GCS control failsafe settings
« Reply #16 on: January 13, 2020, 12:56:58 am »
If you're talking about this method call.

timeDifferenceMs(lastActivityTime, lastSysTime);

I've already inspected it and it just records the time between iterations of the while(1) loop. Unless I'm missing something, there's no way to know if a new ManualControlCommand was received. There doesn't seem to be a time stamp on ManualControlCommand or in UAVObjMetadata.

Re: GCS control failsafe settings
« Reply #17 on: January 13, 2020, 02:28:14 am »
The top of the loop in receiver.c

Code: [Select]
#define UPDATE_PERIOD_MS                 20
...
    while (1) {
        // Wait until next update
        vTaskDelayUntil(&lastSysTime, UPDATE_PERIOD_MS / portTICK_RATE_MS);

So this loop gets run every 20 milliseconds it looks like.  So if you want a 2 second timeout, reset a new counter to zero every time you get new data and increment it otherwise.  If the new counter hits 1000 100, then you have counted a 2 second timeout.
« Last Edit: January 13, 2020, 04:41:43 am by TheOtherCliff »

Re: GCS control failsafe settings
« Reply #18 on: January 13, 2020, 03:31:21 am »
I think we're missing something the other is saying. The loop runs with or without receiving data. How would I tell if the ManualControlCommand object was refreshed since the last loop or not? It's the 'every time you get new data' part I'm struggling with because I'm not receiving events, I'm polling the ManualControlCommand object

Re: GCS control failsafe settings
« Reply #19 on: January 13, 2020, 04:40:44 am »
You use (replacing objectName and ObjectName appropriately) this code one time during initialization.  Look at the first few functions in the source file e.g. receiver.c and you will see the objects inited and callbacks setup there:
static void objectNameCb(UAVObjEvent *ev);
...
ObjectNameInitialize();
...
ObjectNameConnectCallback(objectNameCb);

With the above code, objectNameCb() gets called every time ObjectName gets modified.

librepilot/1609 $ find . -iname '*.c' -exec grep -Hi ManualControlCommandConnectCallback \{\} \;
./flight/modules/ManualControl/manualcontrol.c:    ManualControlCommandConnectCallback(commandUpdatedCb);
./flight/modules/Stabilization/stabilization.c:    ManualControlCommandConnectCallback(FlightModeSwitchUpdatedCb);

Consider callbacks to be asynchronous interrupt functions and you will be OK.  Your callback should probably set flags rather than for instance actually doing a failsafe timeout from there.  To be pedantic, you should make sure your counter is atomic and marked volatile.

============================

or (hacks that would not be allowed in release code) you find where the ManualControlCommand object gets received and reset the counter there or reset the counter in one of the already-coded ManualControlCommand callbacks.

============================

In receiver.c, SettingsUpdatedCb() gets called if either VtolPathFollowerSettings or SystemSettings get modified because of these lines:
Code: [Select]
    VtolPathFollowerSettingsConnectCallback(&SettingsUpdatedCb);
    ...
    SystemSettingsConnectCallback(&SettingsUpdatedCb);

============================

Make sure you never code it to poll continuously (without sleeping).  It's best to wake up every time there is data ready in a queue (but woe is you if your multi byte data packet ever gets out of sync).  It's second best to sleep the correct length of time, and poll each time you wake up, but that can have a sliding delay with hiccup if you sleep say 20ms and the data arrives every 21ms (or worse yet 19ms and you loose some data).  Your particular code and timeout is not critical that way though.
« Last Edit: January 13, 2020, 05:13:26 am by TheOtherCliff »

Re: GCS control failsafe settings
« Reply #20 on: January 13, 2020, 05:32:27 pm »
That was the missing piece. Registering my own ManualControlCommandConnectCallback that updates a last lastMCCUpdate variable. Is xTaskGetTickCount() the best way to read the system clock?

Code: [Select]
static volatile portTickType lastMCCUpdate;

static void MCCUpdatedCb(__attribute__((unused)) UAVObjEvent *ev) {
lastMCCUpdate = xTaskGetTickCount();
}

Utilizing the timeDifferenceMs function to compare lastMCCUpdate with lastSysTime within the main receiver.c while loop gives me the failsafe behavior I need to continue testing my application. Thank you so much for your help.

Re: GCS control failsafe settings
« Reply #21 on: January 13, 2020, 11:20:10 pm »
Glad to help.  :)

It's not really important at a low frequency such as 50Hz, but I would increment a counter rather than make a function call.  Other devs might do differently.  I would have to research whether the fn call was just a memory read / return or whether other stuff (e.g. executive call / task or context switch) was a CPU suck.

static void MCCUpdatedCb(__attribute__((unused)) UAVObjEvent *ev) {
  counter = 0;
}

#define FAILSAFE_TIMEOUT 2000 /* milliseconds */

  counter += UPDATE_PERIOD_MS;
  if (counter > FAILSAFE_TIMEOUT) {
    // do failsafe stuff
  }