Design of handler for System.on() using firmware_update_pending

I am looking for advice about how to design the operation of the handler for the system event firmware_update_pending. I have a temporary solution in place that has allowed me to prove that firmware updates had occasionally left the index files on the SD card corrupted. Issue solved, now looking to have a robust solution implemented.

I am using SYSTEM_THREAD(ENABLED). I assume the event handler runs on the system thread? If it is running on the application thread then the design is less complicated.

I am setting System.on() in setup() thus:

    System.on(firmware_update_pending, handle_restart_events);    //register event handler
    System.disableUpdates();            //by default do not allow firmware updates until handler has stopped processes
    isFirmwareUpdatePending = false;

The outline of the handler declared before setup() is as follows. I have included event and param as function inputs in case I want to add handling other events in future.

void handle_restart_events(system_event_t event, int param)
{
    Serial.printlnf("got event %d with value %d", event, param);
    if (event == firmware_update_pending)
    {
        Serial.printlnf("stop SD file read/write activity", event, param);
        isFirmwareUpdatePending = true;     //signal any SD activity should not open files
        unsigned long SDtimeout = millis();
        while (isSDbeingAccessed && (millis()-SDtimeout < 1000) {delay(1);}    //wait until SD not being accessed or timeout
        //tidy up other activity and stop any processing -> FSM state = wait for firmware update
        System.enableUpdates();
    }
}

My uncertainty in the design is knowing that the read and write operations to SD card have been completed and the files closed. One idea is to use 2 global bool flags

volatile bool isSDbeingAccessed;   //set true just before file.open and false just after file.close
volatile bool isFirmwareUpdatePending;   //false until handler sets true and used by SD access function to avoid opening files

Can anyone suggest a better or more robust/elegant solution.

Just seen this thread post System Events param problem from February - I was wondering why the event and param printout was the wrong value 129 and 512 whereas I was expecting 512 and 0. Now I have fixed that I am trying to understand why the flash sometimes happens and sometimes does not.

The handler is now:

void handle_restart_events(system_event_t event, int param)
{
    #if DEBUG
    Serial.printlnf("got event %lu with value %d", (uint32_t)event, param);
    #endif
    if (event == firmware_update_pending)
    {
        #if DEBUG
        Serial.printlnf("stop SD file read/write activity runState %i", runState);
        #endif
        isFirmwareUpdatePending = true;
        unsigned long sdTimeout = millis();
        while (isSDbeingAccessed && (millis() - sdTimeout < 500))
        #if DEBUG
        Serial.println("Finished waiting for SD card activity to stop");
        #endif
        prevRunState = runState;
        runState = D_GOTOWAIT_FOR_FLASH;
        System.enableUpdates();
        #if DEBUG
        Serial.println("Re enabled flashing");
        #endif
    }
}

My debug output is as follows:

got event 512 with value 0
stop SD file read/write activity runState 1
Re enabled flashing
Prepare Wait for flash to happen in goToWaitForFlashController() prevRunState 1
Waiting for flash to happen in waitForFlashController() for 0.0 secs
Waiting for flash to happen in waitForFlashController() for 1.0 secs
Waiting for flash to happen in waitForFlashController() for 2.0 secs
Waiting for flash to happen in waitForFlashController() for 3.0 secs
Waiting for flash to happen in waitForFlashController() for 4.1 secs
Waiting for flash to happen in waitForFlashController() for 5.1 secs
Waiting for flash to happen in waitForFlashController() for 6.1 secs
Waiting for flash to happen in waitForFlashController() for 7.1 secs
Waiting for flash to happen in waitForFlashController() for 8.1 secs
Waiting for flash to happen in waitForFlashController() for 9.1 secs
Waiting for flash to happen in waitForFlashController() for 10.2 secs
Waiting for flash to happen in waitForFlashController() for 11.2 secs
Waiting for flash to happen in waitForFlashController() for 12.2 secs
Waiting for flash to happen in waitForFlashController() for 13.2 secs
Waiting for flash to happen in waitForFlashController() for 14.2 secs
Waiting for flash to happen in waitForFlashController() for 15.3 secs
Waiting for flash to happen in waitForFlashController() for 16.3 secs
Waiting for flash to happen in waitForFlashController() for 17.3 secs
Waiting for flash to happen in waitForFlashController() for 18.3 secs
Waiting for flash to happen in waitForFlashController() for 19.3 secs
Waiting for flash to happen in waitForFlashController() for 20.4 secs
Waiting for flash to happen in waitForFlashController() for 21.4 secs
Waiting for flash to happen in waitForFlashController() for 22.4 secs
Waiting for flash to happen in waitForFlashController() for 23.4 secs
Waiting for flash to happen in waitForFlashController() for 24.4 secs

whilst waiting the LED is breathing cyan - sometimes the flash is successful but most times it just times out.

Whilst I am waiting for the flash to happen I am in a finite state in the main loop. I am calling Particle.process() and then delaying for a second.

    Serial.printlnf("Waiting for flash to happen in waitForFlashController() for %3.1f secs", (float)(millis()-waitms)/1000);
    Particle.process();
    delay(1000);

What is causing this apparent blocking of the flash after it has been System.enableUpdate();?

I’d not use delay(1000). To slow down your serial output you can use millis() but try calling Particle.process() as frequently as possible (although SYSTEM_THREAD(ENABLED) should take care of that, it’s always not to solely rely on that).

@ScruffR Thanks for the reply. I will try that. Another thing I have noticed is that the flash request will often timeout and that will be the first event that has got to the handler which as it stands puts it into waiting for a flash that has been stopped by the server.

Regards one of my original questions

I assume the event handler runs on the system thread?

Is this correct. I tried to include a reference to a struct e.g. param.resumeCause in the handler and the compiler threw that out as int not defined. Are there restrictions on what can be done in the handler just like an ISR?

Reading this I'd say no, it's running on the application thread

If your handler looks as shown above then the error isn't surprising. With a int param you won't be able to access it as param.resumeCause since int is not a struct.
But if the passed param is pointing to a struct, you'd need to declare or cast that pointer accordingly and dereference the struct's field via param->resumeCause

OK - I see you what you are saying.

So the declaration of the handler is before setup() just so the compiler knows about it when it sees System.on() in the setup()?

Why would I be getting this error?
request for member 'resumeCause' in 'param', which is of non-class type 'int'
Elsewhere in my program I can use param.resumeCause = 5; with no issue.

param may be declared multiple times with different data types. When you declare it as int any other definition will be hidden by the local re-definition.

If you intend to subscribe to multiple events which may have varying data types you should go with this function signature stated in the docs

And when accessing the respective parameter you'd do something like this

void handler(system_event_t event, int data, void* moredata) {
  if (event == eventWithStruct) 
    y = ((myStruct*)moredata)->someField;
  else if (event == eventWithInt)
    y = (int)moredata;
}

Duh! Funny how you can’t see your own errors!

I think you are saying the approach is correct?

Now I understand that the handler() is special function in the application. Where does the application flow go after it has finished in the handler function? handler() is called when the system has a monitored event and this could be anywhere in the application and afterwards it returns to the same point in the application like an ISR?

I just need to work out why it appears to miss system events.

I am still getting the flash request timeout on the WebIDE and then the debug output is showing the handler get another event which is immediately followed by the firmware_update and firmware_update_begin. After this is reports firmware_update_progress every 100mS and eventually firmware_update_complete.

This sort of suggests that System.enableUpdates() needs to be called twice for it to work or there is something that is occasionally stopping the System.enableUpdates() from working the first time? No doubt I am missing something here?!

[Edit] Actually appears that setting System.disableUpdates() after the System.on() declaration stops the passing of the firmware_update_pending event. This makes the whole process unusable?

Not just for the sake of replying to my own posts here is what I have found and as reported to me on a support ticket.

The event handler runs on the application thread and can be called at any time in the application thread. Thus, any sort of waiting for completion of activity in the application thread from the handler is not possible.

The model of setting System.disableUpdates(); after System.on(firmware_update_pending, updateEventHandler); then in the event handler enabling Updates [see initial post in this thread] does not work. This was confirmed by @rickkas7. I understood that this is due to the reset event not getting triggered. Using the function System.updatesPending(); was not recommended - it does not properly work.

The approach I have taken is to instead look for the firmware_update event and when the firmware_update_complete state is reported set a global bool flag to indicate that a reset will be pending soon. This can then be used by any functions in the application thread to stop then starting an SD card transaction. After this test and before any SD card .open I am calling System.disableReset() to stop a reset stopping the completion of the SD card read and write activities in an unknown state. Once .close is called I then call System.enableReset().

In the handler there are two reset events processed: reset_pending which does a System.enableReset() and reset which clears the global bool flag that a reset is pending.

I have then added System.disableUpdates() to the entry of all operating states other than standby and System.enableUpdates() to the entry into standby. In this way I can be sure that flashes will only occur when the application is in its standby state and restart after the flash is simple to manage.

Testing with manual flashing and automatic firmware updates via the console prove that this work reliably and the SD index corruption is a thing of the past.

2 Likes