Basic Code Structure

Featured-32

We’re just around the corner from writing code for the modules on the I2C and SPI Education Shield, but before we get into that, I wanted to quickly explain how I structure my code for legibility and ease of use. There are a number of conventions that are followed and some very specific locations some parts need to show up in, but how the code is written and how it’s formatted can take your code from mess to magnificent very easily.

Objectives

  1. Learn that Dan is not a professional developer.
  2. Understand how constants are used to establish names for registers.
  3. See how indentation and white space can be used to increase ease of reading.
  4. Learn general ideas for creating your own functions.

Background
The Arduino Wire Library
Official Arduino Wire Library Reference

Schematic
No schematic is associated with this module.

Setup
No setup is required.


Basic Locations

Some things just need to go in specific places. When you’re dealing with libraries, you’ll need to make sure that you include the library at the top of your code, and if you need to initialize things, you’ll need to do that in the setup. You’ll also want to create a title block at the top of your code to remind yourself a year down the road what it is your code does. My personal preference is to place any functions I write, below the main loop(), but that’s not a hard and fast rule. I have not found any useful way of organizing the functions themselves in any way that isn’t just as difficult to work with in the Arduino IDE as any other.

Making Register Values Stand Out

When you start working with more advanced chips, you’ll discover that they have all kinds of registers, and each of them will have a discrete hex address that you won’t be able to remember. These will never change when your code is processing, so it’s best to create constants to use in your code. I also use this for establishing the I2C address of the chip I’m using. The constant declarations go between your includes and your setup.

When you’re looking at the following code, notice that the constant names are all different lengths and the constant type definitions are different lengths. If you don’t use whitespace to help your eyes, it looks like this…

Now if we use a little tab’ing and a little extra space, we can make this way more easy to read by eliminating the jumbled mess…

As we move into different chips on the Education Shield, you’ll discover Operation Codes as well as registers that will also need their own constants assigned. I prepend the constant names with “REG_” or “OPCODE_” to tell them apart.

More Examples Of White Space

I use the white space trick all over when I can. It makes everything much easier to comprehend. Here is an example of debug serial output before white space and after white space.

Yes. Funk levels are measured in degrees of Bootsy Collins.

When to Function and When Not to Function

Functions are magic creatures that can sometimes be too small, sometimes be too big, and very occasionally, be exactly the write size to bring order out of chaos.

Typically, when you write to an I2C chip, you’ll need to issue four commands: You send the I2C address, you tell the chip which register you’ll be writing to, you send the register value, and then you kick it all off by issuing the endTransmission() command. Again, notice the use of white space…

General if a discrete bunch of commands is only four well packaged lines long, I’m ok with leaving it in the body of the loop, because it’s easy to recognize, especially if I only use it once. However, if I call it multiple times in different areas, that’s a good reason to break it off into it’s own function.

Additionally, if you’re going to need to use the code when your Arduino is acting like a slave component, then you must have a function ready to declare in the onReceive() and onRequest() commands.

Finally, any setup routine that I need, which typically involves multiple configuration assignments across several registers and conceivably several chips, will always receive it’s own function called either “SetupMyChip” or “InitMyChip” so that it is neatly contained away from everything else, including intrusive variables that aren’t properly scoped.