Make Your Own Arduboy Game: Part 3 - Storing Data & Loops

This is Part 3 in a series on learning how to program your own Arduboy game. If you have skipped the previous parts, please read over Part 1 and Part 2!

Variables

Computers work a lot with calculations and data. To make most video games, you’re going to need to be able to store data, like high scores, or player location, or lives remaining. In order to remember data, a computer must set aside some memory to put that data into. Then, the computer can be told to change the data stored there.

Loops

Remember how I said that computers have to be given specific instructions? Ever notice how the back of shampoo bottles say to 1. Lather , 2. Rinse , 3. Repeat ? If a computer were given those instructions, they would be stuck in an infinite loop of lathering, rinsing, and repeating. In programming, having instructions repeat in a loop can be very useful. I’ll show you how to use the loop() function to do this.

Getting Started

In this program, we’re going to make the Arduboy keep track of a number and display it to you as it is increased.

Let’s do it! Grab the code from the previous tutorial so that we can build off of it:

//Jonathan Holmes (crait)
//October 18th, 2016
//Printing Text

#include <Arduboy2.h>
Arduboy2 arduboy;

void setup() {
  // put your setup code here, to run once:
  arduboy.begin();
  arduboy.clear();
  arduboy.print("Holmes is cool!");
  arduboy.display();
}

void loop() {
  // put your main code here, to run repeatedly:

}

Initialization

Whenever you create a variable, you have to initialize it, which is setting aside memory for the data and giving it a name. You’ve already done this but didn’t realize it. Check out the Arduboy arduboy; line. You initialized an object called arduboy . Objects are a lil’ more complex than I want to get into during this tutorial, but there are different kinds of variables that you can initialize. Here are some: Integers, booleans, characters, objects, doubles, and many more.

We’ll initialize our number that’s going to increase as an integer. This basically means that it’s a whole number, without fractions. Integers will appear as int inside of C++ code.

To initialize a variable, you must put 3 things: The type of variable, the name of the variable, then a semi-colon. Let’s call our variable counter . Here’s the code: int counter; Put it under the Arduboy arduboy; line.

Assignment

Whenever you create a variable, you can give it a value. This is called assignment . We don’t know what counter 's value is because we never gave it one.

Let’s clean up the code by removing the arduboy.print(); and arduboy.display() functions from setup() . Instead, let’s put the assignment there:

counter = 0;

This line of code is saying that counter 's value is now equal to 0. Instead of 0, you could put another number, a mathematical formula, or some other things that I’ll explain below.

Here’s what your code should look like:

//Jonathan Holmes (crait)
//October 18th, 2016
//Counter

#include <Arduboy2.h>
Arduboy2 arduboy;

int counter;

void setup() {
  // put your setup code here, to run once:
  arduboy.begin();
  arduboy.clear();
  counter = 0;
}

void loop() {
  // put your main code here, to run repeatedly:

}

Incrementing

Okay, our program will be repeating a few simple instructions over and over again. Basically, we’ll change the value of counter , then display the value of counter , then repeat. To do this, we should add some code into the loop() function.

counter = counter + 1;

This line of code means that you are assigning the value of counter to itself plus 1. Or, in other words, counter 's value is now equal to the value of counter + 1.

Displaying The Variable’s Value

Now that we have the variable increasing, let’s display it! Remember how we used arduboy.print() to print some text to the screen? Well, we can use that same function to display numbers to the screen, too. Add arduboy.print(counter); .

If you were to run this code as it is, now, then the Arduboy’s screen would fill up with numbers. If we’re going to print something new, we need to be sure to erase what was previously on the screen. We need to add in arduboy.clear(); at the beginning of loop() and arduboy.display(); at the end to display the updated screen.

Have you ever used a type writer? Whenever you type letters, the cursor moves over. The arduboy.print() function works similarly. Every time you use the arduboy.print() function, it moves the cursor over. So we need to reset the cursor to the top of the screen with arduboy.setCursor(0, 0); . I will explain this more in a later tutorial, but just throw that at the top of the loop() .

The Completed Code

I’ve added in some comments, but your code should look like the following:

//Jonathan Holmes (crait)
//October 18th, 2016
//Printing Text

//Include the Arduboy Library
#include <Arduboy2.h>
//Initialize the arduboy object
Arduboy2 arduboy;
//Initialize our counter variable
int counter;
//The setup() function runs once when you turn your Arduboy on
void setup() {
  //Start the Arduboy properly and display the Arduboy logo
  arduboy.begin();
  //Get rid of the Arduboy logo and clear the screen
  arduboy.clear();
  //Assign our counter variable to be equal to 0
  counter = 0;
}
//The loop() function repeats forever after setup() is done
void loop() {
  //Clear whatever is printed on the screen
  arduboy.clear();
  //Move the cursor back to the top-left of the screen
  arduboy.setCursor(0, 0);
  //Increase counter's value by 1
  counter = counter + 1;
  //Print out the value of counter
  arduboy.print(counter);
  //Refresh the screen to show whatever's printed to it
  arduboy.display();
}

Running The Code

Connect your Arduboy to your computer and upload this code to it. Your Arduboy should start counting up!

What’s Next?

In the next tutorial, we’ll be learning about testing the values of variables, booleans, and pressing buttons! Click here to go to Part 4!

Credits

I wrote this tutorial in order to give back to the programming community that taught me to get into it about 10 years ago. If you’d like to follow me on Twitter, please do so at http://www.twitter.com/crait . I’d greatly appreciate that. :smile:

8 Likes

Thanks crait for your valuable tutorial,i appreciate you…

Just started working my way through these tutorials.

I noticed after I completed this part that if I leave the counter running for long enough (not a particularly long time though) it starts showing negative numbers. Is this normal? Should it not be always increasing?

The counter is defined as an ‘int’ which is a signed, 16-bit number. Thus its range is -32,768 to 32,767.

When you have an integer and keep adding to it, eventually it hits the maximum that the integer can handle. At that point it wraps around to the start of the range and starts increasing again - its called overflow!

So for a signed integer once it hits the max, it wraps around to -32,768 and starts counting up again.

2 Likes

Without getting into how binary works, the simplest way to think of the problem is that you need memory to keep track of the digits, and if you kept counting forever then eventually you’d find a number that would require more digits than your computer has memory for.

Consequently, programmers tend to use fixed-size numbers with known limits and very carefully avoid going outside those limits.

Either that or they use fixed-size numbers and completely neglect to avoid going outside those limits and then (most probably) end up with bugs like this one. See also: Pac-man’s level 256 glitch, the Y2K problem and the 2038 problem.

Minor caveat: It’s a signed 16-bit number on Arduboy.
Other systems may vary, terms and conditions apply.

1 Like

Yes that is true. It was probably going into deeper territory describing that in this response though.

Its one of my dislikes in this tutorial. Everything is defined as ints!

I’ve taken to always including that caveat straight away simply because the number of people who don’t know that int varies in size depending on the target CPU is quite staggering.

Even just knowing that other systems might be different is better than being given the impression that it’s the same for all systems. The latter will undoubtedly end in confusion at a later date.

I agree. The ‘stdint’ types are the way to go for modern code. Especially for Arduboy.

2 Likes

Thanks for the info in your replies. Interesting and useful. I’m already familiar with binary so I understand what’s going on now that you explain it. :grinning:

Hadn’t heard of the 2038 one before though, going to have a quick read at that.

So if I were to simply change ‘int’ to ‘stdint’ in the code would the numbers go up further?

1 Like

It’s essentially the same problem as the Y2K bug (i.e. the method chosen to represent the time has resulted in an insufficient range of values), but happening in 2038 because a different date was used for the ‘zero point’ (more technically the ‘epoch’, and specifically the Unix epoch) of the system it affects.

I brought up that one simply because it’s one of the more notable ones that we’ll be running into in the future, but there’s an entire class of problems like this.

No, ‘stdint’ is a header file that contains several well-defined integer data types.

It’s normally #include <cstdint> for C++, but because Arduino uses the C standard library rather than the C++ standard library (despite actually being compiled as C++), it would be #include <stdint.h> for Arduboy.

For a full list of types you can look here.

In this case Arduboy’s int is equivalent to int16_t, so using int16_t would give you the same behaviour.
Changing it to uint16_t (unsigned 16-bit) would let you go up to 65,535 (216-1) before the value rolled over to 0.

uint32_t would give you 4,294,967,295 (232-1) as a maximum.
uint64_t would give you 18,446,744,073,709,551,615 (264-1) as a maximum.
uintmax_t is probably the same as uint64_t (I’ve never checked what it is on Arduboy), but if it weren’t it might allow you to reach a higher number (e.g. potentially 2128-1 on some systems).

It’s possible to go higher than that, but not with the types provided by the Arduino library or C standard library, you’d have to look to see what else is available or write a custom type. For most Arduboy use cases you’re never going to need anything that massive though. Even 64-bit numbers are usually more than enough for most use cases.

2 Likes

Decided to try those out (increasing the counter value by more than one to speed it up a bit :laughing:).

I managed to get it up to ‘uint32_t’ but when I tried 64 I got an error message.

call of overloaded 'print(uint64_t&)' is ambiguous

The code also seems to work OK without the set cursor part. Though I’m guessing it’s good practice to put it in there for certain situations.

Ah, of course. I forgot there’s no print overloads for the 64 bit types.

If you really wanted to do that, there's an easy (albeit slightly expensive) way to do it.

If you include the following two functions:

inline void print64(Print & printer, uint64_t value)
{
	// If value is zero, print zero
	if(value == 0)
	{
		printer.print('0');
		return;
	}
	
	// Allocate space for the 20 possible decimal digits
	uint8_t[20] digits;
	
	// The number of digits extracted
	size_t digit_count = 0;
	
	// The current value
	uint64_t current_value = value;

	// While the current value is greater than zero
	while(current_value > 0)
	{
		// Extract the least decimal digit
		digits[digit_count] = (current_value % 10);
		
		// Decrease the value by a power of 10
		current_value /= 10;
		
		// Count the digit added
		++digit_count;
	}
	
	// For all digits counted
	for(size_t count = digit_count; count > 0; --count)
	{
		// Calculate the index into the digits array
		size_t index = (count - 1);
		
		// Derive the appropriate character
		char digit = ('0' + digits[index]);
		
		// Print the character
		printer.print(digit);
	}
}

inline void print64(Print & printer, int64_t value)
{
	// If negative
	if(value < 0)
	{
		// Print minus sign
		printer.print('-');
		
		// Negate the value and print it as if it were positive
		print64(printer, static_cast<uint64_t>(-value));
	}
	// If the value is positive
	else
	{
		// Defer to the uint64_t implementation
		print64(printer, static_cast<uint64_t>(value));
	}
}

Then you could do:

uint64_t some_large_value = 281474976710656;
print64(arduboy, some_large_value);

(Note that this code is completely untested so it might not actually compile if I’ve mistyped something.)

It’s not really ‘good practice’ as such.

arduboy.clear() always resets the cursor position back to (0, 0) so doing arduboy.setCursor(0, 0) immediately after arduboy.clear() is always redundant.

Also:

  • You don’t have to put arduboy.clear() in setup() if you’re also calling arduboy.clear() at the start of loop() because that means the screen will be cleared before you draw anything anyway.
  • You don’t have to set counter to 0 in setup() because global variables are always set to 0 unless otherwise specified. (The same is not true for local variables.)
  • It’s worth mentioning that the above code neglects to include the important if(!arduboy.nextFrame()) return; line at the start of loop(), which means the frame rate is actually unlimited here, not 60fps as you might expect, but I’m presuming one of the later tutorials covers that.
1 Like