Tips on writing efficient code for Arduino

So you are concerned about efficiency of code space, not execution time or RAM usage. Well the good news is that C code on ARM compiles to fairly compact assembler. While the "blink" sketch can use a couple of K, that's because the core code and libraries take up some space. Adding code to the sketch will not increase the size proportionally - it will add much less.

Would I be correct in thinking resources titled "optimizing C code for Assembler" would be a good start?

A few quick experiments at the limit of my own knowledge - such as trying to write clever loops results in more efficient C code, but the compiled size basically doesn't change.

The resource you mentioned is probably concerned about efficiency of execution time, not code space. While the two can be related, they will probably trade larger code for quicker execution when possible. For example, branch instructions on some architectures can take more than one cycle to execute. So one way to avoid them is "unrollling" loops, which adds to the size of the code but may be faster to execute.

Most of the "clever" things you can do in C do not have much impact on the resulting code size because the compiler is also clever. It recognizes common inefficient code and uses known strategies to optimize it. You can make your C code much more compact (and less readable) but it will most likely turn out about the same size assembly code.

There are a few basic things that can save lots of code space in a sketch. They are not really clever hacks but just sensible precautions. If you really get into a squeeze for space, you can go back over your code and try to optimize smaller things. Meanwhile, just be sensible and write code that is readable and works.

Tip 1: omit unneeded headers/libraries. If you are not going to use a library, then don't include it. The core functions are pretty isolated, so if you don't use one it won't get linked in, but each library has some infrastructure that takes up space. So consider carefully before adding yet another library.

Tip 2: Avoid floating point. 90% of the time that you think you need a float variable, you can actually do just fine with a variation of int. For example, to store the temperature accurate to a tenth of a degree you can use an int. Just make your internal units "tenths of a degree" and use integer operations. The only time you need to worry is when displaying - and you can just calculate the whole and tenth degrees and display them separately.
A single float operation takes up a lot more code space than a few integer ops. Even if you are just storing a value as float, converting from int to float and back also takes up space.

Tip 3: don't make types bigger than they need to be. This is especially true with arrays. Not only should you make the index as small as possible, but the value type should be no larger than needed as well. An array of longs takes up 4 times the space as an array of bytes. But don't use something too small, or you will have overflow and wrap-around problems. Pick the right size.

Tip 4: Keeps strings short and to the point. Omit unnecessary words, use well understood abbreviations and don't over explain things. "Choose from one of the following options:" can
be cut down to the more concise "Choose an option", the terser "Choose one" or just "Options". For debug and error messages, include enough information to make the problem clear, but don't waste space on annoying the user.

Tip 5: Use functions. If you have to write the same 5 or more lines of code more than once, put it in a function. If the same 3 lines come up more than twice, it's time for a function. Function calls are not free, but they are a great way keep code small. Small variations on the same code can be done as a function by passing in flags.

Tip 6: Don't copy data unnecessarily. It takes up more code space, more time and more memory. Especially with structures and objects, try to avoid making "temp" copies. When sending to a function, pass by reference instead of by value especially when the structure/object is large.

For the rest of it, trust the compiler to produce efficient code. Don't worry about micro-optimizations until you actually run out of space. Chances are, with 32K of program space, you'll hit other limitations first.

HTH,