Complex applications code can be hard to understand. This makes it buggy and hard to test, debug, fix, maintain, and enhance. A common part of this problem is one of the features that makes Arduino so easy to use for simple programs: the functions setup() and loop().
I usually write a program by starting simple. Strip what I want to the barest essentials, and then write a little code to do it. Then I extend it, adding a bit more. Add a device: add a little code to setup and loop to control it. Need debug hooks: add more code. Need to enable and disable the device: add more code. Need the device to interact with other devices: add more code. Add another device: repeat. Pretty soon, setup and loop are a maze of twisty passages all alike.
So, what to do?
Organize your code into related bits and pieces by using C++ classes. A class abstracts away the details of how the device works, and how the code interacts with the device, with functions that provide a high level view of the device. But, a class is usually not enough. You need an object of that class to do anything.
For example, the built in object Serial is an instance of class HardwareSerial. This class abstracts away the details of how the Atmega 8/168 does serial I/O by providing functions to read bytes and write ints, char*'s, etc. Other classes might abstract.
Other classes might represent switches, potentiometers, accelerometers, utrasonic distance sensors, LED's, motors, etc.
Usually you can classify devices as inputs or outputs. Switches, potentiometers, and accelerometers are inputs, while LED's and motors are outputs.
Normally, we change what the outputs are doing based on the readings we get from the inputs. This is the program's control logic. An example of a control behavior is: if the front bumper switch has not been pressed recently, accelerate the speed of left and right motor so we go forward more quickly. Another control behavior: after the front bumper switch has been pressed, back up for one second, rotate in place for one complete revolution taking readings from a distance sensor, continue rotating back to the orientation where the farthest reading was taken, then go forward again.
When control logic from a dozen different behaviors is all written within loop(), it can be almost impossible to figure what is going on.
Classes can be used to encapsulate control logic. The Behavior class does exactly this. It can handle simple behaviors like those described above, and by adding a few more abilities supports much more complex behaviors.
Behaviors can be enabled and disabled. A disabled behavior is not run. For example, the FrontBumperHit behavior is probably usually disabled, while the DriveForward, AccelerateIfFrontBumperNotHitForAWhile, and WatchFrontBumper behaviors are usually enabled. When the WatchFrontBumper behavior sees that the front bumper has been hit, it disables the DriveForward behavior and enables the FrontBumperHit behavior. When the FrontBumperHit behavior is completed, it disables itself. And when WatchFrontBumper sees that FrontBumperHit is disabled, it enables DriveForward.
Behaviors can delay when the next run. Normally every enabled behavior runs every time loop() is called. Delay() delays the next time the behavior runs.
SetEnabled, SetDisabled, and Delay and can be called by the behavior itself, by some other behavior, or from any other piece of code in the program, permitting quite complex system behaviors, with quite simple code.
The Behavior library is VERY rough. A first draft at best. Much needs to be done to improve it. In particular it needs more documentation and some examples of how to use it. A few more abilities would be useful:
* Run frequency, expressed either as milliseconds or as a loop count. * Inhibition, automatically disable a behavior if some other behaviors are enabled. * Excitation, automatically enable a behavior if some other behaviors are enabled.
Limited as it is, I've found the behavior class extremely useful and want to share it. Let me know of any bugs, foibles, or features you'd like.