It is said that good coding practice is dying as a consequence of Moore's Law.
But this isn't quite true in embedded computing. Three days worth of kudzu code is now taking up a massive 9 KB of my available 12 KB of program memory. Fortunately there are only a couple of functions left to write.
In a sense, they are still right. I could write better-looking, more elegant, more compact and more ingenious code (not that these are necessarily the same thing!) But it is more important to finish this box and take the lessons learned. And if I have to, I have an ATmega328P lying around that would double my available program space.
It is also necessarily sprawling because of what I am trying to simulate; a combination of analog electronics and (shhh!) future high-tech. So there are a lot of places where it "looks" right to blink a display three times, pause, then go to something else. And getting the timing right on all these events is a bit of a pain. And leads to a lot of nested loops.
Plus the need for transparency; to be able to switch states in the middle of a display sequence without the box throwing up garbage on the display. To catch that is taking a whole lot of little weenie "If (so is still true) then (delete that and clean up)" lines of code.
Physical computing is kind of cool in that when you write a good routine, something on your desktop moves or lights up or makes a sound.
It is also, well, kinda cool in a different way that if you write a bad routine...well let's just say that in the physical computing universe, HCF opcodes (Halt and Catch Fire) are not mere legends! It is especially easy when playing with servos to rip apart a model (or just strip gears) because you typed the wrong variable by mistake.
(For true terror, there is a design for a high voltage boost converter that uses the microcomputer itself as an oscillator. Take a look at the Ice Tube clock! And, yes...that is the design I'll be using next time I build around these lovely vacuum fluorescent displays).
So, anyhow, a big chunk of today was troubleshooting the code that allows the selector switch to be changed in the middle of the simulated threat sequence. Being able to flick from silent to speaker on was easy. What took the time was figuring out why I couldn't exit the sequence entirely when the selector was dialed to "off" or "test."
One problem appears to be that calling a higher-level function from within a nested function doesn't always work properly. It looks as if I can only jump out one level at a time. So I couldn't get from the "read_rotary()" function back out to "loop()" (the Arduino equivalent of "main()"). I could, however, set a flag inside "read_rotary()" and on the routine to "sim_chem()" test for the flag and jump out to the base of the program.
And that solved the "switch the CBR Kit off in the middle of a simulated chemical attack" problem. But then it was resetting the simulated attack when all I wanted to do was flick the selector from speaker on to speaker off.
Which was essentially a switch debounce problem. I figured that out pretty quickly!
Which is a nice little lesson it doesn't hurt to get again. When you are staring at a bit of code on something that interacts with human beings, it is far too easy to think of it as "Now I press the button. And the next time the loop runs..."
No, actually. The computer is MUCH faster than that. It will run through hundreds if not thousands of program cycles before your finger even finishes pressing down on that button. To computers, we humans lead that fine vegetable existence; vaster than empires and more slow.
Plus, of course, the rotary switches I am using are "non shorting." Which is to say; they intentionally go to "no contact" between positions. And "no contact" is the same as the low end of my analogRead.
Fortunately, my code is nearly as slow as the lover in the Andrew Marvell poem. A mere fifty cycles sufficed to detect the difference between "I'm just bouncing around a little here" and "I'm really truly on a different setting now." Which I accomplished through the rather crude "increment a flag every time the switch claims it is something other than where we left it, and if the flag gets over 50, zero it and execute." Oh, yes, and two other clean-up lines for the silent/not silent setting; "Yes, that's what we were fiddling with the switch for, you can put that flag back to zero."
Hrm. I don't think in-the-form-of-dialog-code is ever going to surpass pseudo-code as a communications tool.
Anyhow. Self-test routine is done, simulated chemical attack is done, just have to write the simulated radiological attack and the injector routine. The rad attack re-uses much of the boiler-plate (and several complete functions) of the chem attack -- the main wrinkle it adds is that instead of the light blinking (and the speaker "bipping") whenever the detector sees a significant spike in activity, it will instead act like a Geiger Counter. Which may be the only time I actually use the PWM setting on the indicator lights!
I'm still contemplating when the "injectable" light should light up -- the original intent was that it blinks as soon as a threat is discovered for which the Universal Antidote is good. Sigh. I may just have to add one more counter in the mix in order to make that blink happen consistently between phases of the simulation.
Hrm. Actually, in silent mode the device looks far too static in the final phase of the simulation (when it is actually sounding the alarm.) This is probably a good place to turn on one more blinking LED. I still need to dim that LED. It is too bright to look 1970's vintage. I meant to hang it on a PWM pin but I ran out.