Now that all the modules are complete, let’s see how they’re tied together in main.

Above is the start of main, which consists mostly of constructors. In order are the main timer, load regulator, temperature regulator, user interface, and debugger constructors. Note that the temperature regulator, user interface and debugger take the main timer as an argument, as the main timer is used by those modules to know when it should run its code. Additionally, the user interface and debugger take in the load regulator and temperature regulator, as the two modules send commands to and receive information from the regulators.
After the constructors, the sleep mode is set up. If none of the modules need to be run, then I don’t want the microcontroller to be running through the loop over and over again looking for something to do. Instead, after one iteration through the while loop, the microcontroller will go to sleep and wake up when any interrupt occurs.
Next is enabling the global interrupts, allowing the timers to run, as well as calibrating the zero current for the load regulator. Note that calibration makes the current through the load zero, which is probably a good thing since you don’t want the system to be sinking power when it’s not fully awake.

Next is the infinite while loop. Thanks to burying all the complex code inside modules, the loop itself is very simple. First is the running the load regulator. The load regulator checks the current through the load, and the voltage across it, and then takes action based on those two values. Next is the temperature regulator: it checks the load temperature, and then increases or decreases the fan speed as necessary in automatic mode, or sets the fan to a set speed in manual mode. After that is the UI module: it checks for user input, modifies the system if necessary, and updates the LCD screen.
Now comes something slightly different: the fail-safe. In the future, as I’m modifying the modules, it’s possible that I break something and risk damaging the load. For example, maybe I change the temperature-to-duty-cycle equation in the temperature regulator and make the fan ineffective, or the load regulator makes the load dissipate way more power than it should. In either case, it’s good to have a hard-stop built into the system. In this case, if the temperature reported by the temperature regulator exceeds a specific temperature, then the current through the load is immediately set to zero, regardless of the state of all other modules.
Next is the debugger module. The debugger outputs useful information over serial, and then reads and acts on commands it receives. Note that the debugger may not be compiled, depending on the value of DEBUG. If it’s not compiled, then it’s one less thing for the system to worry about.
Lastly is setting the CPU to sleep and waking it up. sleep_enable shouldn’t be interrupted, so cli disables interrupts, and then sei enables it. Then, since sleep has been enabled, sleep_cpu puts the CPU into whatever sleep mode was set by set_sleep_mode. The system will then halt at this command until an interrupt occurs; in this case, it’ll be until the user inputs a command through serial or encoder, or a timer interrupt occurs. Either way, this allows the system to save a little bit of power by preventing the CPU from running when there’s no need.
When an interrupt occurs, the system moves on from sleep_cpu and disables sleep mode. sei is also called again to make sure that interrupts are enabled.
That’s the main! Now that all code has been written, let’s do some testing!