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


(Scott) #12

So to actually recover the 3K, a sketch would provide it’s own main()?

I thought maybe you would want to make it easier, like adding a library function called minimalMain(), so the sketch would just do:

int main() {
  Arduboy2::minimalMain();
}

minimalMain() would be a static function so it could be called as above, without needing to create the Arduboy2 object beforehand. The minimalMain() function could also include the test for the “enter bootloader” button at its start.

Since the goal of this is to save memory, I wouldn’t add all the code to display text. Just use the RGB LED, as is currently done with the “system control” function. We could light it magenta (which works even with the units with the RGB LED reversed) to indicate “bootloader initiated”.


(Josh Goebel) #13

OH. I don’t love the name, but that’s a great idea. So you just include that boilerplate and do everything else normally… the only thing that bugs me about this is that now the bootloader check is happening even before .start() which wouldn’t happen until setup() was called… one benefit though is that this would easily prevent the bootloader button from even getting compiled in if you weren’t using the smaller main.

I really wanted to show a little something on the screen considering we’re getting 3kb back. We made room for 190 bytes for a logo seems we could make a few bytes room for some 8 pixel high text pushed directly out over SPI (making no assumption about what type of display mode you might decide to use later).

I’m not sure the LED can stay lit after you transfer control to the bootloader. You’d need some testing. If so that’s OK I guess.


(Josh Goebel) #14

Another alternative is showing the existing bootlogo but inverting the screen. One psychological thing here is that people get stressed when they think they might have bricked their device. Being able to immediately trigger bootloader mode via software AND even see the screen light up and show a graphic would reassure “all is well” better than an LED - at least in my mind.


(Scott) #15

No, you never seem to like my function names, and of course the name is negotiable. I just quickly chose something that describes what it does.

The existing boot logo is optional. Some sketches use boot() to save memory by eliminating the boot logo code. I think lighting the LED is sufficient.

However, if you want to put something on the screen, we could have a second function. You would use minimalMain() for just the LED or minimalMainWithScreenText() for the extra on screen indication. (Yes, we would come up with a better name :wink:)

Possibly not. The bootloader might set all the pins to inputs (I haven’t looked into this). We could still light the LED and then call delay() for a second or so before jumping to the bootloader, for visual feedback.


(Josh Goebel) #16

My goal was consistency of experience.

No one has answered the big question though of if the 3kb of space is worth a slightly different reflashing procedure and whether that would confuse any of our users or not.


(Holmes) #17

I always use flashlight mode to flash something on the Arduboy. If there was a way to force the use of that in order to save space, I wouldn’t be opposed to it. :stuck_out_tongue:


(Darrell) #18

I’ll risk getting chastised for commenting, but I think it would confuse a good portion of the average users. Sure, most all the devs and advanced users will get it right away, but from looking over the other threads involving flashlight mode (or even bootloaders), most don’t get it. I’m guessing if a developer really wanted that extra 3kb, they’d need to crystal clear up front how the end user would load a new game later on. Sounds like the VCP would not be there the next time you plugged the Arduboy in and tried to upload a sketch via the IDE.


(Scott) #19

Well it’s certain to cause many “I loaded this game and now I can’t load any new ones” posts to the forum, just as sketches that clobber the magic bootloader number in RAM do now. This could be alleviated somewhat by making sure that it’s documented in “big red letters” with the documentation for every game that requires it. We could also add instructions in the troubleshooting section of the Quick Start Guide and the “I’m having problem uploading, can you help?” FAQ on the web site.

The good thing is that using the button all the time wouldn’t hurt anything, so it might get to the point that people always do it, just as @crait has said he does with flashlight mode.

As to whether there would be times that gaining 3K would outweigh the disadvantages, I really can’t say.


(Gavin Atkin) #20

@MLXXXp I was meaning to wait mostly for text or graphics somehow instructing the user at that point without necessarily taking much away from the logo. The button press could happen sooner. I would just want it to be painfully obvious to anyone how to re-flash with this system in place. If it’s like flashlight mode, we’d get non-stop posts asking how to flash again after one of these sketches has been loaded up.

If the instructions on how to do it could be completely clear (or at least mostly so) without needing to look anything up I think this could be a really great way to free space and there wouldn’t be any reason to do things the old way.


(Josh Goebel) #21

I think developers should play with this first and get a feel for how annoying it would (or wouldn’t be). I won’t have much time next week but after that I’ll whip up a quick demo (unless someone wants to beat me to it) of invoking the bootloader from software. Should be as simple as jumping to it (with asm) or more authentically setting the magic memory location and then triggering a watchdog reset.


(Scott) #22

How many different languages should we support for these instructions? :wink:
So far, the executable code in the library is language agnostic. There’s no hard coded output text anywhere.

On thing to consider is that once the bootloader is invoked you would only have the 8 second window to manually start your compile/upload. Therefore, you would be faced with all the timing problems that you get when using the reset button.

With the “old way” (even if flashlight mode is necessary), you don’t have timing problems because the loader on the PC is able to issue a reset, to invoke the bootloader, at the exact time required.


(Scott) #23

It’s probably best to look at the USB code we’re removing to see how it invokes the bootloader, and use the same technique.


(Josh Goebel) #24

That might prove a requirement - I’d have to look at the bootloader code again. It might check for the type of boot.

Yeah, that is actually the most annoying thing. There is a way to fix that (and have an unlimited delay) but it would require having the program “destroy itself” by writing 0xFFFF to the beginning of Flash. That permanently enables bootloader mode with no timeout.


(Josh Goebel) #25

What I’m learning is that there is a lot to like about the Arduinos with dedicated USB co-processors.


(Kevin) #26

My thought is that it’s ok to build out this functionality but its use should be deemed “experimental” or “alternative” as mentioned previously, it can create confusion for some users. The main concept is user experience.

It sets a magic number in ram and invokes the AVR command for watchdog reset AFAIK


(Josh Goebel) #27

Yes, but that there may be smaller ways to accomplish the same thing… like setting the magic # and jumping.


(Scott) #28

Yes, you may be able to get away with this. I believe the reason for using the watchdog timer is to provide a 120ms delay for the USB sessions to clean up properly before invoking the bootloader. Since we’re eliminating the USB code, we wouldn’t need this delay, so could just jump to the bootloader immediately.


#29

Sorry for bumping this topic. But it’s a great way of gaining ~3K of space. Maybe it gives the Arduventure guys a break on byte crunching?

I looked into catarina bootloader,avr documentation, played around a while and came to the conclusion that using the watchdog timer to trigger the reset is the most convenient way (considering different size bootloaders). I came up with the following code and added it to ButtonsState() in arduboy2core.cpp

Added include at the top:

#include <avr/wdt.h>

Added code to buttonsState():

  //bootloader button combo
  if (buttons == (LEFT_BUTTON | UP_BUTTON | A_BUTTON | B_BUTTON))
  { //set magic boot key
    *(uint8_t *)0x0800 = 0x77;
	*(uint8_t *)0x0801 = 0x77;
	//enable and trigger watchdog by timeout
	wdt_enable(WDTO_15MS); 
	for (;;);
  }

Pressing LEFT+UP+A+B will reset the Arduboy into bootloader mode. The above code compiles to 36 bytes (in combination with my optimized buttonsState() version)

My new version of buttonsState() looks like this:

uint8_t Arduboy2Core::buttonsState()
{
  uint8_t buttons;

  // using ports here is ~100 bytes smaller than digitalRead()
#ifdef AB_DEVKIT
  // down, left, up
  buttons = ((~PINB) & B01110000);
  // right button
  //buttons = buttons | (((~PINC) & B01000000) >> 4);
  if ((PINC & B01000000) == 0) buttons |= 0x04; //compiles to shorter and faster code
  // A and B
  //buttons = buttons | (((~PINF) & B11000000) >> 6);
  if ((PINF & B10000000) == 0) buttons |= 0x02; //compiles to shorter and faster code
  if ((PINF & B01000000) == 0) buttons |= 0x01; 
#elif defined(ARDUBOY_10)
  // up, right, left, down
  buttons = ((~PINF) & B11110000);
  // A (left)
  //buttons = buttons | (((~PINE) & B01000000) >> 3);
  if ((PINE & B01000000) == 0) {buttons |= 0x08;} //compiles to shorter and faster code
  // B (right)
  // buttons = buttons | (((~PINB) & B00010000) >> 2);
  if ((PINB & B00010000) == 0) {buttons |= 0x04;} //compiles to shorter and faster code
#endif
  //bootloader button combo
  if (buttons == (LEFT_BUTTON | UP_BUTTON | A_BUTTON | B_BUTTON))
  { //set magic boot key
    *(uint8_t *)0x0800 = 0x77;
	*(uint8_t *)0x0801 = 0x77;
	//enable and trigger watchdog by timeout
	wdt_enable(WDTO_15MS); 
	for (;;);
  }
  return buttons;
}

(Kevin) #30

I think the plan there is to have a menu option to set it into bootloader mode?

But really the reset duration window is really small, a lot of users have problems trying to actually find the timing correctly when using the reset button. This is a bit scary from a UX perspective because there will undoubtedly be a lot of people complaining they aren’t able to reload different code on their device.

One thing I’ve been considering, and maybe if this is needed for Arduventure, but to completely remove the bootloader timeout from within the reset mode, so that if you press the reset button, it stays in bootloader mode indefinitely. Potentially also flashing our RGB led to further indicate it’s state.


(Scott) #31

This would be good but the problem is that there are thousands of Arduboys already out there without this feature in the bootloader. Arduventure has been promised to run on current hardware, so any solution that requires a modified bootloader isn’t viable.