I've read once some book that was in fashion, about TDD (Test Driven Design). Write a test for every line seems nonsense to me. Who's testing the test, then? I recall even from one of the working places where a software team tried TDD. Very often the tests were failing because of bugs in the tests, and after struggling with the TDD paradigm for a couple of weeks, they ended up being less religious about TDD, and they only kept a few test that were making sense. It was a huge software project, and those guys were top programmers at a multinational, not amateurs.
I'm confused what should I test, and how. Many of my projs have some hardware, so I guess it will take considerable effort to mock the hardware signals.
I'm a pragmatist. I use tests to find out. Mad scientist with his experiments, perhaps.
For example,
in another thread, I'm discussing with Sparkydog about how to control one or two 12V 4-pin PWM fans at constant RPM using minimal circuitry. As typical fans age, the PWM duty does not stay constant wrt. airflow/RPM, so using a small MCU to give a startup pulse and then control the fan RPM via its tachometer output (two pulses per rotation), with a FAN-FAULT signal to the rest of the board in case one or both stall, makes a lot of sense.
Let's say I decide to write such a firmware for some 8-bit AVR, perhaps ATtiny85. (I do that sort of stuff for free, only asking them to pass the help forwards.)
How do I go about it?
The circuitry needed is simple, and I've already simulated it. I only need two cheap N-channel MOSFETs (NX138AKR is my favourite, assuming hand-soldered board) and a couple of resistors, and a bypass capacitor for the microcontroller. An MCU with a timer peripheral with two output channels (most AVRs!) is needed, so that the 25kHz PWM output is handled by the peripheral.
The "trick" is how the tachometer inputs are handled. The pulse interval varies between say 0.1s (100ms, 300 RPM) and 0.01s (10ms, 3000 RPM). Not only are the two fans completely unrelated, but you want to find out if either one or both are stalled, i.e. pulse interval exceeds the maximum limit.
We can do this using input edge interrupts, or using an innermost busy loop counting loop iterations as "ticks". If we use an innermost busy loop, each tachometer measurement can take up to 0.3s (because we need to detect a pair of pulse edges from each fan). That is more than sufficient for the PWM update rate, as the fan blades have significant inertia, and the response to PWM changes isn't instantaneous anyway.
I would start by testing the two method of two-fan tachometer reading: the interrupt method, and the counting loop iterations method.
Both have their upsides and their downsides. Also recall that to get say 8-bit precision at the 25 kHz PWM, we need the PWM timer base to be at least 6.4 MHz; or say 4 MHz, for duty cycle control between 48 (30%) and 159 (100%-1). Thus, the AVR MCU clock will have to be 4 MHz or higher in any case.
(The simulations I've already done to find out the electrical requirements and suitable components could be counted as the first "testing" I did.)
As the controller wants to provide an initial start pulse to get the fan moving, I must then consider how the overarching logic of controlling the target tachometer reading, towards which the fan PWM will be adjusted. In this case, it is straightforward: it is done after each tachometer sampling, as an outer main loop, iterating at least once every 0.3 seconds, but possibly as often as once every 0.01 seconds. So, this outer loop does need some kind of way to reliably measure time intervals under half a second, so it can tell when PWM adjusting needs to be done or not. It does not need to be precise, however, so even knowing the inner loop iteration count or similar will suffice; also so would an 8-bit one-second timer (with 4ms resolution), as long as it is under the outer main loop control and not related to PWM.
Next, I would separately implement the PWM control, and test that. Note that some AVRs have only one 16-bit timer/counter (with two PWM outputs at different duty cycles but the same frequency), and no cycle counter, so Arduino-style millis() or micros() functionality requires a separate timer/counter, or interaction with the PWM clock. I would rather avoid having an interrupt fire 25000 times a second on a small AVR, though.
Next, I would combine the above, and check the interactions. At every stage, I make sure there are no bugs (so no "I'll fix that later"!), and no compiler warnings. This minimizes the regions where bugs may lurk at, and makes it easier and faster to handle the interactions.
Along the way, as I "test" things, I record my assumptions and findings as documentation. This means that when I get to this point, I should only need to collect the documentation, write some glue text (introduction, licensing, etc.), and the project is done.
I'd probably also create a variant of the firmware that instead of the FAN-FAULT signal, uses a potentiometer to control the fan RPMs (with each fan having their minimum and maximum RPMs hardcoded in the firmware). That would be useful for others, too, which is a large part of why I'm considering writing the above code.
I have absolutely zero idea how long doing the above should take. I know I
should know, as I definitely have the experience, but I just do not: it is one of my very significant faults. At least I'm now old enough to report my progress correctly (and only because I learned to write out my plan like this above, and describe how much of each stage has been completed, and what unexpected stuff I discovered); I used to fail a lot at that, too.
While this may or may not help, the above is what I meant by "testing". It is as much exploration, as it is verification; and I use it not just to test the code, but my own understanding of the problem and the behaviour and limitations of the solution model I'm proposing.