Implications of customized system firmware - Photon

Hi @bdub @peekay123 & @zachary ,

My questions/concerns focus around the issue of customized system firmware.

As you know I was trying to solve a problem for my device of endless listening mode on the photon see Photon Listening mode

I have the fix complete. Within the system code I poll the time and flip a relay to turn off the device if timeout is tripped. I know this is kind of crude and does not return control to the user program but that is all I need.

But the real question is what are the implications to making that change for my product going forward?

If I execute an OTA update of my user code in the future will there be inherent problems?

When particle does an update to the system firmware if I do an OTA update of my user code will that overwrite the modified system firmware I have on the device making me lose the modification?

Can I cause my device to be updated OTA to a newly customized system firmware that I might want to make?

Going to need more details here... are you killing power to the Photon? If so could you instead just call System.reset(). Both of these are really not ideal, and I'd recommend spending more time trying to create a proper listening mode software timeout.

I'm going to see what @zachary has to say about your following questions :wink:

Hi @BDub,

Yes I am cutting power to the device. The user can at a later point turn the device back on and it should endup back in listening mode so that they can pass credentials. I know it is rather down and dirty. I could spend more time on it but there is lots to be done and so so little time. Obviously the real time it will take to solve this elegantly will be to learn the internal structure of particle’s code.

Good question @HardWater!

If you build the code locally (as you are now) and use the cloud to OTA flash a binary you generate yourself, you’ll have no problem whatsoever. :+1:

If on the other hand you build code in the cloud, then you are exactly right — the compiled binary would naturally not have your changes. Definitely a gotcha to be aware of.

Onward! :smile:

1 Like

As a quick fix, the custom firmware should be fine for you in the short term.

In the long term, I suggest sticking with stock system firmware, and instead trying alternate approaches:

  • either moving the code from system into user code. The user code has full access to all the hardware, so there should be no hindrances to doing this.

  • submitting a patch so that any missing functionality that is general enough can be incorporated into the system firmware, incuding of course any fixes to any issues that are preventing you from getting the functionality you want.

I’d like to hear more details about the fix and why it needs to be part of system firmware, so we can work out a plan to avoid patching the system firmware. :smile:

1 Like

Hi @zachary

Thanks for the reply. One of the questions I asked I did not get an answer for, it is what if I have a new modified system firmware in the future will I be able to update it to a device OTA just like a user code update?

Hi @mdma

I like your idea about moving any changes into the user code space to avoid this whole problem from the start. I have not tried to work on that at all. The reason being is the modification I need is in the listening mode loop.

As the listening mode loop is called somewhat automatically when credentials are cleared or non-existent from the start I did not think it to be a good candidate for modification in the user code.

I felt that making changes at this level where there is lots of unknown functionality could be rather dangerous and require lots of testing. While a simple if statement placed at the right spot is something that does not have the same risks of creating unknown consequences somewhere else.

I have included a snippet of the system_network.cpp where I have made the change. The modification starts somewhere in the vicinity of line 125 in the code.

       else
        {   /* part of the original system_network.cpp */ 
            uint32_t now = millis();
            if ((now-start)>250)          
               {
               LED_Toggle(LED_RGB);
               start = now;

               if (ext_led_state)  /* start of modified code */
                  {  /* turn off led */
                  pinMode(D1, INPUT);
                  ext_led_state = 0;
                  }
               else
                  {  /* turn on led */
                  pinMode(D1, OUTPUT);
                  digitalWrite(D1, HIGH);
                  ext_led_state = 1;
                  }
               }

            if (now > listen_timeout) 
               {
               Serial.println("Timeout just tripped");

               /* turn off relay to power down device */
               pinMode(D7, OUTPUT);
               digitalWrite(D7, HIGH);
               /* should not be able to get here as power should be lost */ 
               delay(10000);  
               pinMode(D7, INPUT);
               }                 /* end of modified code */

            wifi_creds_reader.read();
        }

@mdma if you see a clear way to be able to get this functionality and not have me mucking around in the system firmware I would love to hear or see it. What are your thoughts on this?

Good questions — this is tricky stuff to get right. :wink:

The user firmware module binary includes an indication of which version of system firmware it depends on. If you type “s” over serial in listening mode you’ll get some cryptic JSON output describing the currently installed modules.

We’re working on automatic dependency resolution in the cloud right now. Within the next two weeks, we will deploy cloud changes that will send a sequence of OTA flashes of the system modules required by a user module automatically when dependencies are unmet. This will happen, for example, when you OTA flash a user app that depends on system modules version 3 if your device currently has system modules version 2.

Additionally, our compile service will, in about 2 weeks, be able to build against a variety of user-selectable platforms. This includes the Photon/P0, the Core, and the P1 of course, but it can also include other custom builds, even possibly from different repos. Exactly how we’ll roll this out or price it is still to be determined, but we are pushing hard on these features at the moment. Many cool things to come.

As you head into production, we can definitely provide what you need to keep your changes on your products, but you should continue to stay in close touch with @jonlogan.

One other thing to be aware of: you can build with MODULAR=n in order to have one giant, monolithic binary instead of the separate user and system firmware modules. This means each OTA always contains all your changes, but it also makes the OTA flashes take a loooong time, minimum about 90 seconds instead of 2 seconds for just the user module.

Running monolithic firmware can get an OTA of monolithic firmware or an OTA of modular firmware concatenated together into a monolithic file to make the switch to modular. Modular firmware, however, can only get an OTA of modular firmware — you can’t go back to monolithic. If you want to go the monolithic route, you’ll need to stay monolithic until you’re ready to switch to modular permanently.

All of the above is a good set of reasons to get the changes into user code only without having to modify the system firmware. If you can make a pull request to the firmware repo of a generally usable feature for listening mode timeouts, e.g., as an additional argument to WiFi.listen() that would not only make your life easier but would help the whole Particle community as well.

Lots of info. Hope it’s not too much. Keep asking questions. Cheers!

1 Like

@zachary Thank you for the excellent response.

You have given me a lot of useful information to try and figure out what direction I am going to take on these issues.

I plan to add an optional flag to WiFi.connect() that can disable automatic listen mode on no credentials. Would that help? User code can then check with WiFi.connecting() and WiFi.hasCredentials() and WiFi.ready() what state the wifi connection is in.

Listen mode timeouts sounds like a good idea. PRs for that very welcome!

Cheers,
mat.

Thanks Mat @mdma No I do not think the flag helps my situation.

I do not know how doable it is but one of the ideas I had was it would be nice if the listening loop would call a timeout function that could exist in the user code. A fixed function name like maybe check_listen_timeout(). For most users the function would be empty and would return without impact other then a little processor time. For others like me they can put whatever they want in that loop to achieve the timeout functionality they need. I would also put my blinking external led code in there.

1 Like

That’s a nice idea! It fits in well with system events, where the application firmware can register a system event handler to be notified of system activities. https://github.com/spark/firmware/issues/279

we could have wifi_listening event that is called during each iteration of the listening loop so application firmware can take action such as implementing a timeout, external LED control etc…

This is simple to code and document - will let you know when it’s in the develop branch.

3 Likes

The develop branch now contains a preliminary implementation of system events. Here’s a test app that programmatically enables listening mode, and turns it off after 3 seconds. If the user then presses the mode button to enter listening mode, it’s also exited after 3 seconds.

#include "application.h"

SYSTEM_MODE(SEMI_AUTOMATIC);

void listen(system_event_t event, uint32_t param, void* pointer)
{
    if (event==wifi_listen_update)
    {
        // exit listen mode after 3 seconds
        if (param>3000) {
            WiFi.listen(false); 
        }
    }
}

void setup()
{
    WiFi.on();
    System.on(wifi_listen, listen);
    WiFi.listen();
}

void loop()
{
}

Does that address your needs for custom system firmware?

10 Likes

System Events? Great move @mdma ! Gets my vote!

If the right events are implemented, then a lot of issues could be overcome whilst reducing the need for customised firmware (the subject of this trail) for certain applications.

For example, to assist with deployment issues, RGB LED changes could be caught and reflected in some other UI (say an offboard RGB LED).

If it doesn’t take too much or your time, could you provide a list of proposed events?

@mdma I really like this solution I tested it. It does exactly what I need. I also I think it can help OP do what they might want EXCELLENT!

I have a few questions to followup:

  1. I made a couple successive changes to the listen function when I recompiled and flashed each to the device. The system seemed to be rebuilt and flashed as well as the user code. Is that what would be expected?

  2. If so do you see difficulty down the road with the system updates OTA?

  3. Lastly, When realistically would this feature be part of the production codebase for cloud compiling?

Glad to hear it works for you, that was my hope! :smile:

  • to answer 1 with a question, what make command are you running?
  • system updates OTA is being worked on now and to be released during this sprint (2 weeks)
  • the next firmware release will also be during this sprint. We are getting ourselves in a position to be able to release firmware every few weeks.
1 Like

@mdma

This is the make command I am using.

make clean all PLATFORM=photon APP=photon program-dfu

Should it be something else?

This all sounds good.

For that, we have set_rgb_led_handler.

I'm maintaining an event list in the issue description. I don't expect it to be complete, but continually evolving. If you have suggestions feel free to add to the issue!

Which directory do you run that from?

1 Like

Hi @mdma Your thinking that I was maybe in the wrong directory is completely correct. Today for whatever reason I was recompiling from the modules directory not the main directory as I should have been. I just recompiled a small change to the user code from the main directory all that was flashed to the device was the user code.

Thanks for pointing the way

Great we got that resolved! Yep, running make in modules builds all modules for the platform (system and application code). Running in main builds only the application code.

2 Likes