Disable reset/debug menus on boot

Posted on
Page
of 2
/ 2
Next
  • Hello,

    I’m trying to make my Bangle ‘foolproof’ for when I give it to users to test my app.

    I’ve got this bit of code in my app which prevents them resetting it on a long press (and it works great):
    watchdogInterval = setInterval(() => E.kickWatchdog(), 1000);
    This also prevents the screen with the ‘btn1 = reset ===>’ and the black menu with all the options to factory reset, reboot, restart, turn off, etc.

    The only issue is if the user let’s the Bangle runs out of power and they charge it back up, instead of ‘clicking’ the button to turn it back on…. they might attempt to ‘hold’ the button down instead… and this may inadvertently get them to the btn1 = reset ===> / black menu screens by mistake…. (as the app hasn’t had a chance to load)

    So I was wondering if there was a relatively painless way to just stop these menus / screens from being accessed by the user at all?

    Thanks,
    Stuart

  • Hi Stuart,

    I'm afraid there isn't a painless option - it's built into the firmware precisely to provide a foolproof way to get back to a known state!

    However it was built into the firmware quite recently - I think only 2v19 so if you used 2v18 then you wouldn't get it.

    Similarly you could actually build your own firmware... It's not that hard and can be done online:

    When you commit that change after a few minutes you should be able to go to https://github.com/espruino/Espruino/act­ions (in your account), click on your commit and find the modified firmware which you can then upload yourself

  • Hi,
    I later tried going with the Github method you suggested but encountered a snag when trying to 'Flash from file'. There is probably something super obvious I'm missing as I don't really know my way around Github. These are the steps I followed (see attached images):

    I tried uploading the .zip artefact file entitled BANGLEJS2.zip to the IDE. I also tried unzipping and re-zipping it.

    I noticed a downloaded .zip of v20 from https://www.espruino.com/Download is 520kb but the BANGLEJS2.zip (from GitHub) is 318kb but if I unzip it I get a folder called espruino_2v20.70_banglejs2 which is 514kb and if I compress that it again (with the same folder title + .zip) it is still 318kb. You can see the folder has the relevant .bin .dat .json files and the manifest.json reads:
    {

    "manifest": {
        "application": {
            "bin_file": "espruino_2v20.70_banglejs2_app.bin",
            "dat_file": "espruino_2v20.70_banglejs2_app.dat"
        }
    }
    

    }

    I'm on a Mac if that makes a difference.


    5 Attachments

    • a.png
    • b.png
    • c.png
    • d.png
    • e.png
  • You need to upload zip file espruino_2v20.70_banglejs2.zip which is inside zip that github makes. Maybe that is the option you did not try? Do not unzip/rezip the second one, just use it as is.

    MACOS puts some extra stuff into zips when making them so this may confuse the code enumerating the data inside. Also Nordic DFU does not use compression on zips but this is not problem here as Nordic code is not used for these updates via Firmware Update app.

  • I unzipped BANGLEJS2.zip .... which gives me the unzipped folder espruino_2v20.70_banglejs2 ... I can then rezip it to give me espruino_2v20.70_banglejs2.zip ... but trying to upload any of these to the IDE gives me "Error: Unable to find manifest, is this a proper DFU package?".

    I've noticed I get the same problem downloading the BANGLEJS2 file (the Artifact Produced during runtime) even from Gordon's GitHub repo https://github.com/espruino/Espruino/act­ions/runs/7709447059

    Again there's probably something obvious that I'm missing.

  • I can then rezip it to give me espruino_2v20.70_banglejs2.zip

    Do not unzip/rezip the second zip, just use it as is.

    Github puts artefacts (=output) of the build into zip file. the result of the Espruino build is Nordic DFU zip file. That's why there is zip inside zip.

  • I just checked and it should be ok - there's no need to re-zip or anything:

    So you just need to unzip BANGLEJS2.zip and then upload the single file that's inside it - which I would do using the Advanced mode of https://banglejs.com/apps/?id=fwupdate not the Web IDE (as generally it's safer/faster).

    If you've got an Android phone you may find it's easier to use that to do the firmware update as it won't pretend that the file inside BANGLEJS2.zip is a folder when it isn't.

  • So you just need to unzip BANGLEJS2.zip and then upload the single file that's inside it

    BTW this is at least second time this happened, maybe it wouldn't be hard for the firmware app to detect zip inside zip and just use the nested one?

  • I've just updated https://github.com/espruino/Espruino/blo­b/master/README_Building.md#super-easy-g­ithub-method to be clearer about the zip.

    maybe it wouldn't be hard for the firmware app to detect zip inside zip and just use the nested one?

    I guess - although it'd need doing in the Bangle.js app loader and the Web IDE, so it's not a super quick thing to do.

    There is also an argument that reflashing your Bangle.js firmware over the air to something you just made is potentially a dangerous thing to do - should it really be any easier than it is now (which is already pretty easy)?

  • Ah actually opening the BANGLEJS2.zip on a Chromebook gave me the necessary folder. That seems to have worked. Cheers.

    I did have another quick question which is whether I could completely disable the watchdog menu (not just when holding the button on startup) by modifying the firmware. I've encountered issuers using:

    watchdogInterval = setInterval(() => E.kickWatchdog(), 1000);

    So any alternative way to avoid the user being able to access debug/system menus would be great. (can I just comment out more code in the fork?)

    Thanks

  • If you remove the if (BTN) lines here so kickWatchdog always runs that'll fix it: https://github.com/espruino/Espruino/blo­b/372d4afa4a9da3ad61ddc15ca32a149d5c4da4­0a/libs/banglejs/jswrap_bangle.c#L1202-L­1206

    But honestly you shouldn't have issues with kickWatchdog if you follow what's at https://www.espruino.com/Bangle.js+Butto­n+Reset

    And if you do remove those lines, you won't even be able to reboot the Bangle except either from code, or if that doesn't work, by waiting until the battery goes flat - so use it with extreme caution!

  • Going back to the original question I'm still getting the screen with: ‘ ===>' if the user holds down the button on start up... it just won't progress to the black and white options menu anymore if they hold it down long enough.

    Should have commenting out:

    if (jshPinGetValue(HOME_BTN_PININDEX))
    recoveryMode = true;

    prevented the ' ===>' screen from appearing (on startup)? Or was your edit just for getting rid of the black and white options menu?

    Might still be a simple mistake on my part.

  • I'm still getting the screen with: ‘ ===>' if the user holds down the button on start up...

    Yes, that's expected. The === bit is the bootloader - replacing that feels like a really bad idea, but if you do the other stuff then as long as they don't flatten the battery and hold the button while applying power, they will never see it.

    Should have commenting out recoveryMode = true; prevented the ' ===>' screen from appearing (on startup)?

    No - it's just the recovery menu.

  • "- replacing that feels like a really bad idea, but if you do the other stuff then as long as they don't flatten the battery and hold the button while applying power, they will never see it."

    Yeh that's still the use-case I'm worried about... Simply because it takes 'more' than a quick click of the button to turn the Bangle on (I typically have to hold for about 1 second to boot)... so if the user's first attempt to turn it on is too short they might overcompensate with a long press (holding for about 2.5 seconds typically starts the bootloader from off).

    I could make a little print off to give to the user before testing with them, detailing what to do if they accidentally trigger the boot loader (they will be with the device for a few weeks unsupervised... and with my app they can expect to need to charge it regularly otherwise the battery will run flat)...

    Unless there's some simple modification I can make to the firmware... I'll probably just go with that.

    Btw is this expected behaviour for the firmware without the recovery menu?: https://www.youtube.com/watch?v=sDSFhLWg­ZKo

    I notice if I allow the ===> to progress all the way to the end and I KEEP holding the button down , it goes to the Expruino splash screen... (where as if I let it go straight away after ===> fills up it initialises my app) .... but it stays at the Espruino splash screen until I go back to the boot loader. It would be handy if the user accidentally gets to that splash screen it would just know to initialise the app (and maybe that's actually the expected behaviour?).

  • Have you thought about increasing the duration required to enter the bootloader? Are they going to be likely to hold the button for 10 seconds eg.?

  • "Have you thought about increasing the duration required to enter the bootloader? Are they going to be likely to hold the button for 10 seconds eg.?"

    If that is possible then it would be a great solution. But it would have to be an increased duration from the very start of the Bangle booting up (which seems like it would need a firmware modification).

  • it would need a firmware modification

    Yes and it is not the main espruino firmware but the DFU bootloader. It is of course possible but if you got that far with customizations I think you should get familiar with SWD debugger and a way how to recover when DFU is not working at all.

    As for solution - what I tried for some devices in the past is to enter bootloader only if device is on charger, in your case it could simply ignore button at boot time when it is not on charger. So people could not enter it by mistake when wearing it. When battery is flat and you put it on charger the button would work when it starts booting but I guess that is not such big issue?

    EDIT: The easiest way to do this quick hack could be changing the get_btn1_state here
    https://github.com/espruino/Espruino/blo­b/master/targets/nrf5x_dfu/hardware.h#L7­6 to report button pressed only if also charger is attached. There already is get_charging_state below it so you could just use in inside get_btn1_state.

  • I could make a little print off to give to the user before testing with them, detailing what to do if they accidentally trigger the boot loader

    Even if they did accidentally enter it, it will time out after ~30s if they just leave it I believe, so you shouldn't need that much documentation!

    As @fanoush says if you want to start messing with the bootloader, you probably want to spend ~£30 on an SWD programmer so you can update the code on the Bangle no matter what happens. If you fiddle with the bootloader without one, if you make any mistake in the code you'll have no way to recover the Bangle.

    is this expected behaviour for the firmware without the recovery menu?

    Ahh, yes - but obviously you don't want that either! You'll need to delete both of the #if defined(BANGLEJS) bits here:

    https://github.com/espruino/Espruino/blo­b/d217500f45277ddcb7631d46c02d9bfdb4bb17­ca/src/jsflash.c#L1372-L1398

    Then the code you wrote will run regardless of what the user does

  • “You'll need to delete both of the ifdef BANGLE bits here”

    Ah that’s working better… seems I can just press and hold on the splash screen to force my flashed app to boot from that screen (as opposed to looping back to the bootloader).
    (my modified code: https://github.com/stuaxe/Espruino/blob/­728409948658a4ff11053cb5a1155cb36a1c95b8­/src/jsflash.c#L1372C1-L1384C1)

    And thanks for letting me know about the 30 second time out on the initial bootloader screen (it’ll be much easier to direct the user to simply ‘wait’ if they see that screen).

    Is there any way for the splash screen to also get that behaviour (the 30 second time out till loading my flashed app)? If not, is it easy enough to modify the splash screen to include an instruction just to hold the button down until the flashed app appears?

  • Is there any way for the splash screen to also get that behaviour

    I could be wrong, but I'd be pretty sure that with those changes you won't ever be able to get into that Splash screen. It should boot straight into your code even if the button is pressed?

  • Unless I modified the code wrong (see link above)... then no that's not the behaviour that I'm seeing...

    Although I now see the word 'Bluetooth' at the bottom with a thin black bar after it (which wasn't there before).

  • How are you writing your app? Just as to flash in the IDE?

    Maybe if you choose Storage and then .boot0 as the file, that will ensure that it's always executed (i think)

  • Yeh I generally flash from ide (execute code at boot).

    I just tried writing directly to .boot0 but having too many glitches to list (it's like it's trying to load the usual Bangle OS as well as my app at the same time, as well as giving me a scrolling screen of bluetooth errors)... do I also need to be modifying and writing the following into the left hand side of the ide:

    require("Storage").write("abc.info",{
    "id":"abc",
    "name":"abc",
    "src":"abc.app.js"
    });

  • Reinstall bootloader from appstore, it uses .boot0 file.

    Now make your app use .boot1 instead of .boot0

    The info file is only for making a normal app that is listed by the app loader app. You might need your app as a clock, and also as a .boot1 file. An app being a clock just means that it is loaded by the bootloader(.boot0 AND .bootcde) App, by default.

    Depending on how your device is reset/loaded, it won't always execute the .bootcde which is the clockLoader. But sometime it will. That is why I suggest a .boot1 and a normal app clock which are identical code. This could be a workaround for you. Ofc there can be other ways to solve this too.

    But this method might cause your code to load twice, in the normal case. So we have to think about it a bit more. Probably you would delete the file : .bootcde if you are relying on .boot1 file as a clock loader instead.

    EDIT: I just realized there is a .bootrst file that overrides .bootcde behaviour, AND runs upon reset too. So instead of deleting .bootcde, and instead of writing to .boot1, you could try writing to .bootrst. Then when you want to revert back to normal behaviour , delete the .bootrst file. Now the Bootloader app's .boot0 will load and then .bootrst (Your App) will load.

    Conslusion: Write your app to .bootrst, the bootloader app(.boot0) should not try to load a clock now. Bear in mind as above has said, all this tinkering can be risky for bricking the device (If you lose access to DFU screen). If you do still have access to DFU screen, you should be fine.

  • Would I be able to fork firmware v19...?
    I'm not sure if one be suitable: https://github.com/espruino/Espruino/act­ions/workflows/build.yml
    as in which one would be the exact same version that I can download here> https://www.espruino.com/binaries/esprui­no_2v19_banglejs2.zip

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Disable reset/debug menus on boot

Posted by Avatar for user156427 @user156427

Actions