Pip-boy 32,000 (Nearly finished)

Hey y’all, so I’ve been working on a lil something the past few days while waiting for a certain green OLED to come in for a new diy Arduboy prototype(you’ll never guess what it is :roll_eyes:). It took a lot of fine detailing (particularly animating vaultboy) but it’s very nearly complete. This game is not a game at all, but in fact a program that turns your Arduboy into a functional Pip-boy! Seeing as it’s based around an atmega32u4, it only seemed appropriate to name it “PIP-BOY 32,000”.

pipboy32u4.ino-arduboy.hex (77.1 KB)

Here’s the rundown:

Four menu groups are presented at the bottom of the screen: INVentory, USB communication, MAP of area, and RADio. Press “B” from any group to view the startup splash animation again.

INV: Keep track of important items by hiding them at coordinates and saving to this screen in sketch. default is the keys to a lock, a lock to a very special triangle, and an “emergency cache” (can you figure out what they are?)

USB: Execute BadUSB attacks on windows or linux machines, or turn your new pip-boy into a temporary game controller for other systems. Neither of the badUSB attacks are malicious by default. Add useful commands for shortcutting around your desktop for dev work! Default has Display windows, Lock windows, and open linux terminal.

MAP: Just a seriously bad quality 1-bit map of Houston. (6/18/21: updated to have a nicer map)

*RAD: Plays Arduboytones versions of songs heard throughout the wasteland.

*Missing one very important feature at the moment: audio waveform visualizer. Graph is present, but there’s nothing being drawn to it, as I’m entirely uncertain about how this would be done the research I’ve done on my own has me even more confused. Any ideas or advice is very welcomed. EDIT: Fake sine wave is now present over graphing locatino. I will not be trying to turn this into w more realistic waveform as it’s beyond my comprehension skills atm, but if anyone thinks they can hack it, be my guest.

Source available here on github.

9 Likes

(Disclaimer: I’m terrible at sound programming…)

The Arduboy tones format stores notes as frequency-duration pairs, with the frequency first and the duration second.

I believe the frequency should give you the wavelength of the (sine?) wave to draw, and the duration should tell you at which point to start rendering the next note.

From what I gather from the documentation there’s only three amplitudes: high, low and silent, so that makes the height of the waves easy to work out.

Depending on what your rendered scale would be like (i.e. what duration your X axis spans), you may have to render multiple notes at once, and if sound playing works the way I’m presuming it does, getting the timing right to match the sound as it plays might be be a bit fiddly.

(I haven’t looked much into what sound interrupts actually do on Arduboy, and I don’t have time to look at the moment. I presume they load a buffer with sound data that’s fed into the speaker at regular intervals since that’s how it usually works on other systems, but it might be different on Arduboy, I don’t know.)


Or, y’know, set it up to send useful commands instead.:P

(+D and +L are eternally useful.)


P.S. Feature request: button to stop the music.

2 Likes

ArduboyTones just toggles the pins digitally. For what the pins are outputting you could just draw square waves (which would be easier than drawing sine waves).

For high volume, one pin is always driven opposite to the other. For normal volume, one pin is held at ground. Therefore, if you’re drawing the voltage output, high volume is twice the amplitude of normal volume.

An interrupt is generated by a timer at twice the frequency of the currently sounding tone. It’s used to toggle the pin(s), thus generating the desired frequency. Also during the interrupt a duration count is decremented to know when the note has ended, to set up the interrupt timer for the next tone (or silence duration, or stop playing).

However, all this is assuming you want to draw the output in the time domain, as you would see if probing the pins with an oscilloscope. You may wish to draw in the frequency domain, as you would see from a spectrum analyzer. Since only one note (frequency) is playing at a time, this would just be a matter of drawing a full or half height bar (the Y axis) at the X axis position corresponding to the frequency of the note (assuming you didn’t want to draw the harmonics generated by the square waves).

2 Likes

I’m not great at sound coding either, most of the time it’s one of those things that takes me a while to get in concept and once I finally do, I have terrible trouble implementing it in real situations to suit my purposes.This is one of those cases. I’m not sure how I would go about actually coding any graphical changes based on the audio being played, as it’s being played, so for now I’ve added a false sine wave animation.

It’s just a sprite of half a wavelength that loops itself, put next to a copy with animation synced up correctly to make it look like a single moving wave. There’s 24 frames to the sprite, so not exactly the ideal solution, but it’s decent aesthetically and the original Fallout games rarely had a real spectrum or audio visualizer, instead the implemented something similar to what I’ve done, but every few seconds would change to a different type of wave. I always found this somewhat disappointing, so I would eventually like to figure out how to properly make some sort of visualizer for this. I experimented with a few methods, taking inspiration from things like HelloNOISE, but ultimately I didn’t have much luck, so the fake sine wave is here to make things more complete until I can figure something out, but it’s not my highest priority with my current projects right now.

I think BadUSB attacks are useful as they’re usually just automated command sequences anyways (the default ones I included are admittedly not very useful, but they do a nice job of showing how to set up a simple badUSB attack in the sketch if someone is unfamiliar). But I can agree that simple commands are very useful as well! To that end I added three more USB commands that can be sent to a receiving computer.
Specifically:

  • +D (display/hide windows)
  • +L (lock screen)
    and
  • Ctrl+Alt+t (Opens terminal in linux)

I also added the ability to toggle the RGB leds on&off as a flashlight while still in-game, so as not to lock things and still have a flashlight. Technically the original pip-boy goes into an over-bright mode and you get a flashlight effect that way, but I think the LED method here works just as well. Press ‘B’ to toggle the flashlight on from any screen and view vault-boy again for a few moments. To toggle the flashlight off, press ‘B’ again to view vault-boy, and press ‘A’ while this screen is visible.

1 Like

I like it (A LOT,) but how much space do you have left for other code? Maybe you could add some additional functionality (notepad? calculator?) that might not be in the actual game, but would still be useful in real life?

1 Like
2 Likes

I just had a look at the source (I was wondering how much memory is being used) and I’m confused about how you’re actually getting this to compile. It seems you’re not actually creating a Joystick_ instance anywhere, so the code won’t compile because the code is trying to reference a Joystick object that doesn’t actually exist.

1 Like

Oh, cool! Never mind, then.

If I’m not mistaken, the Joystick library should create that instance inside itself, so that whenever #include <Joystick.h> is called is when it gets instantiated during compilation. You’ll see in the example code provided by the library, that all that needs to be done is include the library, make sure your buttons are input pullups (which they will be already with arduboy.begin()) and then call Joystick.begin().

The game controller segment of this code is almost unchanged from Eried’s Virtual Gamepad which uses version 1.0 of the Joystick Library to work. Now, in their code, Joystick is instantiated before setup(), but I don’t think this is necessary as far as I can tell. In fact in my own code, I’ve defined JOYSTICK_REPORT_ID as 0x03 which I now realize is unnecessary as well, as it’s already defined inside of Joystick.cpp.

In case I’m wrong though, what compile errors are you getting? I can’t seem to get mine to have any trouble with it.

Well let’s not put that idea to bed just yet. We’ve seen it done in ardutosh, so why not try to implement it into a pip-boy? It is more or less a personal computer, and should probably be able to keep notes and/or do basic calculations! That said, the challenge here will definitely be space. Right now I think there’s about ~500 bytes left for variables, but I haven’t focused at all on optimization when it comes to this code, so there’s probably room for efficiency improvement in many places.

Okay, then! Cool!

That’s the version-1.0 branch.

If you look at the master branch, their examples demonstrate instantiation:

The same is true of the version-2.0 branch, so I presume version 2.0 is where they made that change (which would make sense, that is a breaking change).

Unfortunately the authors don’t appear to include a means to detect the library version, so it’ll be impossible to auto-detect the installed version and adapt to the changes. Without that, I’d recommend targetting the newer version since that’s the version people are more likely to have installed since the IDE always prompts when there’s a library update and I expect most people will just click ‘update’ without worrying about breaking things.

Otherwise if you have some particular reason for sticking with version 1, you should either put a big notice up on the README or actually include it locally and use quote includes instead of angle bracket includes.

Even if it weren’t, your macro definition wouldn’t have any impact on the library anyway…

Libraries that don’t contain templates are pre-compiled by the IDE before any user code is run, meaning you can’t affect them by defining macros before inclusion. (I found this out when attempting to redefine something before including the Arduboy library several years back.)

And even if that weren’t the case, you’re defining the macro after the library is included, so there’s no way it could possibly know about the change even if it wasn’t being precompiled.


There were some other warnings that might be cause for concern, but since I couldn’t compile at the time I didn’t look at them all.

One I know is a definite cause for concern is that you aren’t initialising the local variables in gamecontroller.

If you don’t initialise a variable, they get ‘default initialised’, and for all the primitive types that means they get filled with junk values, not default values as they might in other languages like Java and C#. If you then read those junk values you’re likely to end up with strange bugs that are hard to identify. If it’s working as-is, it’s only by luck, not by design.


The one you’d be best off starting with is to wrap your strings with the F macro, which will put them in flash. By default you’ll only be able to get it to work with print and println, you’ll need some extra ‘magic’ to get your CommandAtRun functions working. If you think that’s needed or if you’re just generally curious, I can explain how the ‘flash string’ trick works in further detail. (Or I can just show you how to do it if you aren’t worried about why it works.)

2 Likes

All good to know! I’m familiar with F macros, I’ll go ahead and work on those changes, including adapting to the newer library version!

1 Like

Alright, I’ve updated everything with those suggestions! The most recent version of the Joystick library is now being used, with Joystick_ being instantiated in the .ino file with the other libraries, including Encoder now (do wish the library had a better name), which allows for screen menu switching using a rotary encoder attached to pins 3 and 0. I went ahead and added F macros in all the places I had missed before, and I updated the map screen to not look so messy. Now it’s actually a decent map of Houston. I also added a short beep when a USB option is selected, as the badUSB attacks don’t have immediate indication that they were selected. Oh, and lastly, I initialized those booleans in gamecontroller()

Right now the IDE says “sketch uses 27982 bytes (94%) of program storage space. Maximum is 29696 bytes.” in case y’all wanted to know how big it is now.

1 Like

This time (after installing the Encoder library) it actually compiles, which is obviously much better.


I take it you’re using a smaller bootloader than the standard Arduboy?

For me it claims:

27788 bytes (96%) of program storage space. Maximum is 28672 bytes.

(Which come to think of it technically means it’s actually compiling ~200 bytes smaller for me… Strange, but probably not worth worrying about.)


The only remaining warnings are an unused variable (newPos in func.h at line 86) and some warnings about the fact you’ve used char * instead of const char * as the arguments to your CommandAtRunBar functions. Those are all easily fixed.

(Assuming you’re using the Arduino IDE, if you haven’t got warnings set to ‘All’, it’s worth doing so. Just go to File > Preferences and set Compiler warnings: to All.)


Are you interested in reducing the memory usage, or allowing the Command functions to handle F-wrapped strings, or any other changes/improvements/suggestions?

Maybe? I do have a mod-chip installed, and I think I remember something about that having a different sized bootloader? I think the ~200 byte difference might be from compiling mine with MrBlinky’s Homemade Arduboy library, set to the standard arduboy.

I didn’t notice this before! must’ve been from when I was still experimenting with encoder controls.

I forget this isn’t default lol, I’ve been using a windows computer lately. Thanks for the reminder!

Both of these would be great to learn about!

1 Like

This is probably the longest reply I’ve written in quite some time…

If you bought an Arduboy FX with the pre-installed FX mod it’ll have a different bootloader.
If you bought the official FX mod and installed it yourself then chances are you also used the on-board control chip to overwrite your Arduboy’s existing bootloader.

You’ll know the difference because it’s the custom bootloader that allows you to load games off the FX chip, so if you had the standard Arduboy bootloader you wouldn’t be able to load FX games.

Either way that’s not going to have any effect on the compiler because the compiler only uses the settings you give it.

That’ll probably be explain the 200 byte difference. It’s probably from the customised ‘wiring’ library.

Are you sure it’s set to ‘standard Arduboy’ though?

If it is, I would have expected it to still be reporting “Maximum is 28672 bytes.” because the ‘standard’ Arduboy has a 4KB bootloader.

The fact your compiler reports “Maximum is 29696 bytes.” implies that it’s being told that the bootloader is only 3KB, which means it’s probably being told you’re using the ‘Cathy3K’ bootloader somehow.

It’s not automatically an issue, and probably won’t be an issue for you because you probably do have the 3KB bootloader, but when your programs are large enough to get near the limit, just remember that people who still have the standard bootloader don’t have that extra 1KB, so if you start eating into that limit you might want to make a note somewhere that your game isn’t compatible with the standard bootloader.

The 1KB difference hasn’t actually been an issue so far because in the past only a few people have overwritten their bootloaders, but with the FX units coming along I expect it’s only a matter of time before we start getting games that rely on the extra 1KB and it becomes more of an issue.

Unfortunately it’s one of the many things Arduino did in an attempt to be more ‘beginner friendly’, and it’s far from the worst thing they’ve done in that respect.

Unfortunately saving memory isn’t just a “do these N things and your program will definitely be smaller” situation. Saving memory is a very situational affair that depends heavily on what your code is doing and whether the compiler’s got there first or not.

There are several dozen different things that usually save memory, but many are situational, they aren’t guaranteed to always save memory and some can even do the opposite in some situations for some reason.

I can’t list everything, but I’ll mention a few simple things:

  • Obviously using F as much as possible is one of those things.
    • There have been occassions where not using F for some calls (but still using F for others) has actually worked out better for reasons unknown.
  • Instead of having lots of branches that each call the same function with different parameters, it’s often cheaper to make the branches set variables and then call the function at the end.
    • This is because function calls are expensive on AVR whilst variable assignments are cheap, so having one call and multiple assignments is often cheaper than having lots of calls.
    • If you know how function calls actually work ‘under the hood’ then this should make even more sense.
  • If a function is being called with the same parameters from multiple branches, it’s worth checking to see if it’s actually always being called, and if so it’s typically cheaper to move it outside the branch (because then you aren’t duplicating the calling code).
    • Sometimes the compiler is smart enough to do this for you, other times it won’t, but either way it makes your code cleaner and easier to read when you make it clear that a function is always called.
  • Using lots of smaller functions instead of a handful of larger functions typically saves memory.
    • The short version of why this is the case is that the compiler finds it easier to optimise smaller blocks of code because it has less to keep track of. The issue isn’t so much the amount of code, but the complexity of ensuring that certain properties continue to hold as the scope increases in size.

One memory-saving suggestion specific to your code:

Instead of maintaining clockwise and anticlockwise as separate variables, if you stored difference as a global you could replace all uses of if(clockwise) with if(difference < 0) and all uses of if(anticlockwise) with if(difference > 0). If you still want decent readability, you could introduce functions that do the same job.

E.g.

bool wasTurnedClockwise()
{
	return (difference < 0);
}

bool wasTurnedAnticlockwise()
{
	return (difference > 0);
}

This wouldn’t be a massive memory saving, but it’s little savings like this that eventually add up. Any kind of redundancy that can be eliminated is a bonus.


One other thing I noticed while looking at your code again which might be a bug...

It seems to me that most (if not all) of those button# variables in gamecontroller probably aren’t doing what you think they’re doing. It seems to me like you’re assuming they’ll keep their value when the function is called again, but they won’t because they’re local variables and local variables are destroyed when a function ends.

If on the other hand they actually are doing what you want them to, then it seems to me that you don’t actually need them because (for example) button2 != pipboy.pressed(UP_BUTTON) will always be equivalent to false != pipboy.pressed(UP_BUTTON) which is effectively the same as just pipboy.pressed(UP_BUTTON). Following that logic, when you do button0 = !button0; and then Joystick.setButton(0, button0); it’s equivalent to Joystick.setButton(0, true);. Like I say, if that wasn’t the intent then you need to make those variables global.


Before I explain ‘F-wrapped strings’, how much do you know about the following?

  • Progmem (and reading from it)
  • Function overloading
  • Type aliases
  • const

(The more you know about these, the less I’ll have to explain. If you don’t know any of these, I’ll have to explain each of them first.)

1 Like

I’m familiar with progmem and const but haven’t done much reading regarding function overloading or type aliases!

Unfortunately I’m not going to have time to write any decent explanations myself for a while, but I’ll leave you some links to read up on the aforementioned topics so you’ll hopefully be read for the F macro explanation when I’ve got time to write it.

I added some const stuff just in case you’ve been lead to think that const actually means ‘constant’ rather than ‘read only’.

If you can’t wait, have a dig through some of my old forum posts using the forum’s search features. I expect I’ve probably written one or two explanations in the past.

1 Like

Will do! Thanks so much :slight_smile:

1 Like