Advice for Arduino programming

Open theremin assembled

Hail Arduino! Arduinos are great. They seem to be the breeding ground for some of the worst code samples I've seen, though.

I wrote this so you can improve at programming. At first you will go into an other extreme if you will follow but you'll figure it out.

Why write about this?

OpenTheremin shield arrived this week and I quickly found out that Arduino MEGA 2560 is pin-incompatible with Arduino UNO. I would have to do some hacking to figure out how to make this work. I decided to do this with Arduino Micro because it's smaller and has built-in USB support.

I would have looked into the firmware's source code sooner or later. It would have been lot later without the incident. I'll eventually want to hook the theremin pitch and volume to the rawhid.

I was not satisfied with the code when I saw it. Gaudi really did few things that badly hampered the readability of this firmware.

I hope you'll learn how to make it better.

Object Oriented Programming

The main problem comes when people not qualified to give advice are giving advice. They tell you that globals are bad, for example.

Then you proceed to create a class "Application" and put everything inside. You even write stubs for setup() and loop() that only call "app.setup()" and "app.loop()".

Afterwards you proceed to write some code that fondles T0 port with a Timer0/Counter0, and there are only one each of these on your platform.

Code that is logical to read from beginning to end and doesn't hop all around is easy to read. But now you have these kind of things in your code:

void loop() {
    app.loop();
}

void Application::initialiseTimer() {
    ihInitializeTimer();
}

Does it look like I want to solve an unrelated maze puzzle while I am figuring out what your program is doing?

Fatbstractions

This nextMode() appears only once in the program and it's one line long. Why is it a procedure? You have a procedure although a comment would fare much better and keep the definition inline.

Invent a new word for everything you see, and you are talking very smooth pig latin.

If you did this, you might get everything written inside a single function. This is bad right? Well no. If there's no reason to have two functions then one function is good.

That one function is 2000 lines long? It's ok. At least you've not been a prick that you would be if you spread it into 20 files that are 200 lines long each.

This also applies to other attempts of abstractions. If there are just two modes NORMAL and MUTE. Do you really need a state transition table to keep track of them?

Indentation consistency

Please indent after a left curly brace and drop it after a right curly. The whole idea of structured programming pivots on this concept that you see where the program repeats in a loop or continues after a condition.

while(qn<31250){
while(!(PIND & (1<<PORTD3)));
qn++;
while((PIND & (1<<PORTD3)));
};

Compare that with this one:

loop_1:
    if(!(qn<31250)) goto loop_1_end;
    while(!(PIND & (1<<PORTD3)));
    qn++;
    while((PIND & (1<<PORTD3)));
    goto loop_1;
loop_1_end:

And finally with:

while(qn<31250) {
    while(!(PIND & (1<<PORTD3)));
    qn++;
    while((PIND & (1<<PORTD3)));
};

I provided these there in the order of how readable they are.

Variable scoping consistency

There's something even worse you can do to hamper readability than mismatching indentation.

You have a loop() that would naturally loop in the first place. How to make it less readable? Here's a trick. First create a goto loop inside this loop:

void Application::loop() {
    loop:

    goto loop;
}

Next you only need to add some local variables, and class variables, and global variables. Use them all together. Force your reader to go through three or four variable definition tables before he figures out every single one of them!

You don't feel like a man if you don't abuse variable scope, right?

Custom libraries for commond needs

It's often nice to write the following code:

digitalWrite(0, HIGH);

But it's going to be 30 to 60 times larger than the single instruction to flip the PD0 up:

PORTD |= 1<<0;

Arduino anything is bulked up and non-optimal. But guess what. It's idiotic to do the optimal thing up-front. Don't accustomize to doing it.

If the EEPROM or other functionality in the Arduino library isn't enough for your needs, please write the reason into the comments of your code why you diverged from the obvious.

When you write code that manipulates pins or data directly, I would prefer you are clear about it. Remember to explain why you had to do it and why you couldn't do the obvious.

That helps a lot.

Public "advice"

The origin of over the half problems in this code come from the numerous preachers in the computing world.

I can name a few. Bjarne Stroustrup and that one guy who wrote a book about SOLID, Michael Feathers. Rasmus Lerdorf, author of PHP. There are plenty of them and you can witness how they retard beginning programmers.

These people aren't doing it because they are evil. They just happen to be simultaneously:

This is the recipe that brought us mandatory type-annotations in dynamic runtime, TTD evangelism, OOP and java, FRP, DRY, premature optimization fancy, forced functional programming, forced separation of code and data, automatic refactoring tools to keep up with the mess, design patterns/architecture, integrated development environments, UML charts...

People acting on their fallacy propose that your code sucks because it's not doing the latest fashion statement. They propose there are "general" or "best practices" you should follow.

There's a simple vaccine for not being affected by preachers. The trick is to never take anything at the face value. Exercise critical reading when you are studying anything.

Never do something just because everyone else is doing it, or that you are told to do it. You should understand the reason for what you are doing.

Similar posts