Idea: Remove the USB stack and regain 3,000 bytes of Flash

I think there’s a misunderstanding here. I was talking about a the possibility in future versions. Not about something that is planned.

I’v started working on a custom bootloader though but there is no plan for it other then diy upgrading.

2 Likes

The key words being

and

:P

1 Like

Continuing the discussion from Hello, Commander:

As has been discussed in this topic, the issue is that with USB removed the user must manually invoke the bootloader (either by using the reset button or via software) in order to upload a new sketch. Unless the user is warned and well informed about what will be involved, there will probably be a lot of “I can’t upload a new game after I loaded this one!” type complaints. So the question is, do the benefits of more code space outweigh the inconvenience and problems that it could present to Arduboy users and people offering technical support?

That being said, I’ve created an experimental noUSB branch of the Arduboy2 library, which has added functions to make it relatively easy.

Here is information on using these new library features:

Added functions are mainNoUSB() and exitToBootloader()
To remove the USB code you provide your own main() function which calls mainNoUSB() like so:

int main() {
  arduboy.mainNoUSB();
  return 0;
}

Function mainNoUSB() will also check if the UP button is pressed when the sketch starts and, if so, will reset to the bootloader to allow uploading a new sketch. By holding down the UP button, the bootloader will be restarted each time it times out. This makes it relatively easy to upload a new sketch (but it still may take a few tries).

mainNoUSB() is the only required function but it will help if a sketch uses exitToBootloader() to make it easier for the user to enter the bootloader after the sketch is running. The idea is that the sketch would include a menu item or some other way to prompt the user for uploading a new sketch. If the user chose this option, something like:
"Press and hold UP to begin the upload or <some other button(s)> to return to the game"
would be displayed. Pressing UP would result in exitToBootloader() being called (which wouldn’t return).

This is a basic example sketch that I used for testing:

#include <Arduboy2.h>

Arduboy2Base arduboy;

int main() {
  arduboy.mainNoUSB();
  return 0;
}

void setup() {
  arduboy.begin();
  arduboy.setFrameRate(30);
}

void loop() {
  if (!arduboy.nextFrame()) {
    return;
  }

  if (arduboy.pressed(UP_BUTTON)) {
    arduboy.exitToBootloader();
  }
}
2 Likes

Thanks for the detailed update. I had seen this thread, but was under the impression that a conclusion hadn’t been reached.

It’s good to see that the noUSB branch solves the code aspect of the problem.

I’ll keep an eye on Evade2 to see if the complaints/inconvenience are an issue. Maybe games with a warning would scare people away? Now we can see.

1 Like

I cannot compile and verify the following claim right now but I believe the noUSB branch as-in will not eliminate all USB related code because the ISR will not be removed by the linker. From memory the ISR and its dependencies make up quite a bit of those “~3kbytes”.

Arduboy2Core::exitToBootloader() is a very good idea.

I see Arduboy2Core::mainNoUSB() is calling Arduboy2Core::exitToBootloader() if the UP button is pressed but, given enough progmem, using Arduboy2Core::exitToBootloader() it would be possible to add an in-game menu item like “press a button to reboot to bootloader” (or a more user-friendly message). EDIT: I just saw this was already described in a previous post.

1 Like

Even so, the library changes free up about 2700 bytes of program memory and about 140 bytes of RAM with very little effort. Removing the ISR might take a fair bit more effort and probably couldn’t be done in the library alone.

EDIT: I’ve looked at the code produced with my example test sketch above and see no sign of any USB related ISRs being compiled in.

1 Like

Which IDE version are you using? I ask because less than two weeks ago when I tried to do the same thing I could not prevent the USB ISR from being linked in unless the Arduino’s USB core source file was removed.

EDIT: I found it was possible if I avoided the IDE and Arduino build system and use makefiles instead.

1 Like

Arduino IDE version 1.8.4 under Ubuntu Linux 16.04

Thank you. I stand corrected, USB ISRs are indeed optimised away when linking using your noUSB branch. The difference between your approach and my attempt is your redefinition of main in the sketch (and not calling initVariant()). Well done! :smile:

2 Likes

Of course, I can’t take credit for this idea. It was proposed in the original post by @Dreamer3

1 Like

Cool! had a peek at the code and I’m in favor of using a macro like:

#define NO_USB __attribute__ ((OS_main)) int main() { \
  init(); \
  UDCON = _BV(DETACH); \
  USBCON = _BV(FRZCLK); \
  power_usb_disable(); \
  setup(); \
  for (;;) loop(); \
  return 0; \
} \

Then if you don’t want USB support you can simply put

NO_USB

somewhere at the top of the sketch. It’s easy to use and will save a few bytes too.

I think checking for a bootloader keycombo in buttonsState will have the smalllest overhead. (You don’t need to initize the button hardware additionally and buttons are scanned in flashlight(), bootlogo() on startup) another advantage will be that you can enter bootloader mode in game and don’t need to turn Arduboy off and on again reducing power switch wear.

Yes, that’s a possibility, but I’d probably call it somthing like ARDUBOY_NO_USB to avoid the possibility that such a simple name is used somewhere else. There have also been some suggestions for other optimisations in comments in the GitHub repository, which I could consider. Firstly, I think it should be decided if allowing USB code elimination is something that should be added to the library at all. (The whole advantages vs. disadvantages thing.)

But I’d kind of like to keep just the UP button by itself as the key sequence. This way it’s the same as flashlight() and safeMode(), which would mean that users would only have to remember to use UP for almost any upload problems. Also, whatever combo was used in buttonState() would no longer be available for sketch use.

We did this for Evade 2. The problem is that you can brick your device if you don’t have tight controls on uploading.

I found that avrdude lied to me about the size and i bricked two devices before i realized what was going on. :frowning:

You lose Serial.print and some other neat features, and I say this process should be used as a last resort.

Yes please do not use this method unless:

  1. You really know what you are doing
  2. You understand this sorta kinda potentially voids the warranty (if you brick your device in this manner, we will try to help but will not replace)
  3. You’ve already run out of all other methods to reduce memory space.
  4. It’s made explicit to the user what steps must be taken to reprogram the device. Instructions should be published with the post of the game but even better is some indication within the game itself.
1 Like

You can only brick units that were shipped without the bootloader protection fuses set. This was an error on the part of the manufacturer. I don’t see how you can hold the user responsible for this.

1 Like

Was this using the standard IDE procedures to update or your own custom update method?

Is it something that’s related to removing the USB code and so couldn’t happen if you use the standard Arduino main()?

I agree.

However, I’m leaning towards including the functionality in the Arduboy2 library. Since the “cat’s out of the bag” on this technique, by adding it to the library at least implementation would be standardised and could be fully documented with all the necessary cautions and recommendations for using it.

3 Likes

It was just an example. But yeah ARDUBOY_NO_USB

Wouldn’t the flashlight feature be unaccessible then? It may also be confusing. I’d propose using DOWN as it’s easy to remember if you want to DOWNload a sketch hold DOWN while turning power on (hence why I used it in my custom bootloader) when using DOWN the flashlight option remains accessible through UP

thats true but an awkward to press button combo would most likely not be used by the sketch and by adding a timer so the combo has to be hold down for a while accidental presses or short presses are ignored.

I think It’s a handly feature during development and for when you don’t want to or have the space for a menu implementation. It could be optional like have it enabled by default and disable it when an menu option is implemented.

Here’s a test of Evade 2 where you can enter the bootloader by holding down L+U+A+B for about a second. (Let’s Use Arduboy’s Bootloader to remember)

The relevant added code to buttonsState():

  //bootloader button combo
  if (buttons == (LEFT_BUTTON | UP_BUTTON | A_BUTTON | B_BUTTON)) 
  {
    button_hold += 1;
    if (button_hold == 30)
    { cli(); //ensure that the magic key set below can't be overwritten by an interrupt
      *(uint8_t *)(MAGIC_KEY_POS + 0) = MAGIC_KEY & 0XFF; //compiler optimizes uint8_t by 1 instruction when LSB and MSB are equal
      *(uint8_t *)(MAGIC_KEY_POS + 1) = MAGIC_KEY >> 8;
      wdt_enable(WDTO_15MS); //enable and trigger watchdog by timeout
      while (true);
    }
  }
  else
  {
    button_hold = 0;
  }

On a side note I also discovered when running Evade 2 on hardware that there is a glitch in the Caterina bootloader: Whenever the bootloader mode is exited. USB interrupts are not disabled and cleared causing Evade 2 to freeze after upload (since there is no USB interrupt vector defined) The glitch also made it into my custom bootloader but it’s fixed there now.

EDIT:
Link to Evade 2 test version removed. The official version is now updated with the bootloader and reset button features.

2 Likes

My assumption is that if you are resorting to removing USB code to free up program memory, then you would be using boot() and not even including flashlight mode. If the sketch did want to have a true flashlight feature it could always be included by adding a function that did the same thing but was invoked from a menu or other button(s).

Except the IDE calls it UPloading a sketch, so the UP button makes more sense.

Adding timer is probably going to produce more code than just detecting UP in mainNoUSB().

There’s no reason you can’t write your own function to replace buttonState() during development.

I reset and disable the USB hardware in my mainNoUSB() function. Do you think this will address the problem?

You’ve got me there :slight_smile:

the LUAB button check and counter in above example compiles to 24 bytes progmem and 1 byte ram for counter.

That’s true. but with this feature added to the library, Existing sketches can be updated with a simple recompile rather than being edited. Final sales pitch: Imagine an Arduboy with flash memory or SD card. You’d want an universal method to return to the bootloader/menu for every sketch :wink:

No it doesn’t completely.But putting

UDIEN = 0;

above init() will. init enables interrupts immediately.

#define ARDUBOY_NO_USB __attribute__ ((OS_main)) int main() { \
  UDIEN = 0; \
  init(); \
  UDCON = _BV(DETACH); \
  USBCON = _BV(FRZCLK); \
  power_usb_disable(); \
  setup(); \
  for (;;) loop(); \
  return 0; \
} 
1 Like