User Access to the MODE Button?

I very seriously doubt that users are switching WiFi networks so often that they need a dedicated button for it. Holding down MODE and tapping RESET takes maybe an extra second. Giving up an entire user accessible button to save that second just seems silly to me. In fact, it might even take the same amount of time or be faster when you think about it.

Right now you have to hold the MODE button for three seconds, but if the default RESET+MODE state is SmartConfig it should be almost instant. Once you let go of RESET just wait for that first blue pulse of the LED and you’re good to go!

I think the best way to go would be this: Move SmartConfig to the bootloader, then add a series of #define statements that will allow a user to control the functionality of the button in their sketches. You say that you use SmartConfig all the time for testing, but with the current issues I’d rather have the MODE button directly reboot into DFU or Restore mode at times. This would solve that.

#define MODE_USER
#define MODE_SMARTCONF
#define MODE_DFU
#define MODE_RESTORE

As it stands, it shouldn’t be hard to solder a wire onto one of the exposed MODE pads or even remove the switch all together. I like the idea of remapping it to one of the inputs, but you’d need a way to write that data into an area that was accessible from the bootloader, since it needs to know what port and pin to look for. This would also solve the issue of automatically going into JTAG or DFU mode without user interaction.

1 Like

We use it often for testing on lots of Wi-Fi networks, but perhaps the everyday user only has one network that they use the Core with all the time. Is that the case? - @zach

I think the use case will be split. The Core is perfect for building mobile devices and as such I think in many cases it will need to be able to travel from network to network.

Ken

There is one important issue here that the bootloader can’t be edited without a JTAG programmer, so if we do make major changes, many users will be left with an old version

Wait, what? You guys don’t have an OTA way to update the bootloader?!

It’s not hard to write, basically a reverse bootloader. The app would contain the new BIN for the bootloader, once it’s running it just overwrites the current BL and reboots. You can deploy this OTA or via DFU just like any other app.

In fact, I was under the impression that DFU mode currently allowed you to overwrite the bootloader? It shows up in the memory map…

FYI, I find myself changing networks constantly with my cores during testing - I would be sad to see that button functionality used otherwise.

I’m not proposing it to be removed… I’m proposing a way for the user to disable it and use the button in their sketch, if they want. :smile:

Why would you need to write to bootloader to expose a function that puts the core in the MODE state? Cant that just be a function that triggers on a interrupt or something similar, all user configurable through the code. In that way we can write a code that kicks the Core into a specific mode once a criteria has been fulfilled such as the user pressing the a button or shaking it three times, all up to you to design how to implement. Then once the user has edited the wifi credentials the Core would restart and you are good to go again.

If you the user decides to not edit the credentials he would just restart the core by power cycling or pressing the reset button.

The current MODE button would still be there as a backup of your code doesn’t make sense and you need to factory reset the Core.

When you power up or reset the Core, it always loads the BL, which checks the state of the MODE button on startup. It’s also responsible for copying the OTA firmware from the SPI flash to internal flash and some other sanity checks.

If there’s nothing for the BL to do it simply loads up the user firmware. However, if the BL sees you’ve got the MODE button held down it puts the Core into DFU or Factory Restore mode. So, let’s say I disabled the SmartConfig functionality from the MODE button to use it as an input in my user sketch, I’d still want the ability to go into SmartConfig mode without re-flashing the Core. To make that possible you’d want to add SmartConfig mode as an option in the BL.

This way users who want to use the MODE button as a general button in their sketches will still have access to SmartConfig mode by the same means you currently put your Core into DFU or Restore mode. Does that make sense?

Im not proposing to remove the functionality of the MODE button. I think that should stay the same and be locked to be used as only a MODE button. For input device it is to small to do anything good in most prototypes and soldering a button to another pin is so easy and cheap which in my mind I think is the better option.

What Im proposing is that we make a function available that takes the Core from the state of running your code to the state of being in SmartConfig mode.

Seems like a lot of rigamaroll (sp?) to gain access to the mode button for general purpose use.

I think the mode button should definitely stay a dedicated button to allow easy access to SmartConfig, DFU, Factory Reset, and more in the future perhaps…

But maybe just come up with an easy way to use the mode button during that first 3 seconds before SmartConfig kicks in. “Tap tap tap… yay, my LED is toggling.”

I’d side with @timb :wink:

Sure it’s not a big deal to wire up a button, but since there is one already, we could save one of the few precious pins for something more sophisticated than just a button.

On the other hand allowing this needn’t mean to remove the original function, if the user could play with the timing for standard functionality at compile time (e.g. #define MODE_USER + #define MODE_SMARTCONF_TIME 5).
If I want to use the MODE button in my sketch, I have to put up with the fact of having to press the button longer (e.g. 5 sec) to have standard functionality - if not, everything stays the same.

You didn’t hear it from me but…

-This code needs a WARNING: This will prevent you from entering listening mode without a factory reset-

void loop() {
    if(BUTTON_GetDebouncedTime(BUTTON1) >= 250) {
        BUTTON_ResetDebouncedState(BUTTON1);  // <-- I interfere with entering listening mode
        RGB.control(true);
        RGB.color(255, 255, 255);
        delay(1000);
        RGB.control(false);
    }

I think you can use the button if you’re fast in your user-code, anything more requires changing code in main.cpp. Keep in mind! This code will prevent the smart config button process from working, and you would need to factory reset your core. :smile:

I agree with what @BDub suggests. One can always quicky spin up a harware multiplexing solution for high i/o demand applications. Also, the whole idea of having the :spark: Core on WiFi + OTA is to never have to touch it again (under ideal conditions ofcourse :slight_smile: ).

Works Great Dave!

void setup() {
    pinMode(D7,OUTPUT); // just a dummy instruction (without this it didn't seem like it wanted to flash the core)
}

void loop() {
    if(BUTTON_GetDebouncedTime(BUTTON1) >= 250) {
        BUTTON_ResetDebouncedState(BUTTON1);
        RGB.control(true);
        RGB.color(255, 255, 255);
        delay(1000);
        RGB.control(false);
    }
}

I can reset and get back into Smart Config just fine by holding the Mode button for a few seconds after reset… am I missing something here? Mode button doesn’t seem to do anything normally after you are running your user application.

EDIT: Oh I see… when my core connects back to the WIFI and CLOUD really fast (less than 3 seconds) it won’t detect run the Smart Config because the user app takes over and resets the debounce time.

Ok with a little bit of code we can pretty cleanly wait for Smart Config to catch before we take over the Mode button :wink:

uint32_t startTime = 0;
void setup() {
    startTime = millis(); // capture the time that our app starts
}

void loop() {
    if(ModeBtnPressed()) {
        RGB.control(true);
        RGB.color(255, 255, 255);
        delay(1000);
        RGB.control(false);
    }
}

bool ModeBtnPressed() {
    if(millis() - startTime > 3000) { // wait at least 3s for smart config
        if(BUTTON_GetDebouncedTime(BUTTON1) >= 250) {
            BUTTON_ResetDebouncedState(BUTTON1);
            return 1;
        }
    }
    return 0;
}

@timb access granted! :smile:

6 Likes

Sorry to resurrect an old thread, but is there anyway to call into the DFU mode from an app? For example it would be very useful for debugging for my app to run and then go into DFU mode so can just rebuild and flash immediately?

@mtnscott, check this out:

1 Like

@mtnscott great question!
Try calling System.bootloader();
---------------------------name subject to change

http://docs.spark.io/firmware/#advanced-system-bootloader

2 Likes

@peekay123, @BDub These are great suggestions and they both work well. I noticed in both cases the reset button no longer works (if I wanted to cancel out of this mode and reboot). Is there a way to enter DFU mode from an application and still have the reset button work as normal?

Yes, satish has already implemented a non-persistant form of this function that we will roll out in the near future. A reset will then clear DFU mode. :smile:

1 Like