Saturday, September 25, 2010

Something I'm Proud Of

Engineers are supposed to make things, right? You'll cruise the hobby blogs and read about all these cool projects that light up and/or tweet things - everyone seems to be making something nowadays. I'm not so great in that respect. I really haven't made anything apart from my job. It's a little embarrassing, but I don't really get off my butt unless someone orders me to do something. But then I'm great - I make scripts. PCBs, code, you name it.

Well it turns out that lately I've actually accomplished something - and it all started with a problem. While working on a microcontroller design at work I was readily infuriated by breadboards. You just can't get away from them and you shouldn't try. Everything really out to be breadboarded before it's finalized - otherwise you'll have loads of incorrect PCBs laying around and lots of wasted money. But breadboards are hard to work with: wires pop out, you can't put surface mount components on them, they don't have nice modular connectors and you can't always stick a probe in the holes to see what's going on. So I made something to fix that problem for me.

This little doohicky is a breadboard test point/connector adapter. It has a row of 8 pins that plugs into your breadboard and then connectors for the power bus (there's two different configurations of power busses that i've seen - .1" pitch and .15". It has holes for both). First, it has 8 surface mount LEDs for each of the pins making it very useful for status displays. Second, it has a 2x5 connector that will fit a standard IDC cable plug. I have plans to make an IDC to male pin cable that plugs into it. Third, it has two sets of test points - there are male headers for logic analyzer female plugs to fit onto (along with the obligatory 9th connection - ground) and then there are high profile test points that work great with scope probes. And also for the scope probes there are four high-profile test point ground connections. No more silly jumper wires to the ground bus on the breadboard. All of the test points are isolated from the breadboard by 10K resistors. That way, if you accidentally short two test points together (perhaps one that is connected to ground and another to +5V) you won't break whatever is connected to those pins. If, for instance, you do have the worst case scenario and +5V shorts to ground, you have an equivalent 20K of resistance between them and nothing short circuits - just 2.5mA flows through the connection. Lastly, there is a 1.5A resettable fuse between the breadboard VCC and the VCC on the IDC connector, so if something you accidentally connect the +5V from the IDC connector to ground the fuse will trip and not hurt your power supply on the breadboard.

I plan to sell these at... well, wherever will take them. Sparkfun, Ladyada, etc But not yet. There are several design problems that need to be ironed out. A bit more parts selection, maybe a few more components and this board will be ready to sell. I hope everyone else is as excited as me.


Anyone? Bueller?

Oh well.

Thursday, September 16, 2010

Don't Save Bad Code

As coders we're often asked to do things we don't know how to. Use a new library, use a new microcontroller, try a new compiler. So we start to experiment - make something 'just to see' if you can program your microcontroller or figure out how a library works or do simple socket communication. Then you think 'Wow! That worked! Let's try more things! I'll just start coding everything here in main()....' And then you get eaten by raptors.

You've made a mistake. You weren't thinking about organizing this mess or even making sure it was robust - you were just seeing what you could do. Your main() is 1000 lines and you're not sure what all of this code is supposed to. You didn't comment because it was all so obvious earlier, but now all you know about this line is that if you comment it out the program crashes. Whoa boy.

But still you feel the compulsion to try to work with this mess. You want to massage it, start breaking things off into functions, add a few defines, rename the variables and make it work again. But step back - is this what you're supposed to be doing? After all, you started this to learn the library or a framework and, well, haven't you? Isn't that information tucked in your head by now? Is this code even remotely close to your final product? Could it be? Does it work? Is it well-organized and robust? Didn't you have a real development plan somewhere here? Complete with projections and unit tests? And haven't you learned enough to start on the real work, rather than just trying to save this one .c file out of sentimentality?

You shouldn't try to save old code just because it's already written. Old code has no particular value over newly-written code for several reasons.

First, there is no cost to writing new code in place of old code. When working with physical objects if you make a mistake with a material you have to pay to replace the material. If I were whittling wood and I shaved off a little too much I would try to work with it. But with software we have source control, so if I make a mistake I can revert it or even start over without losing anything. I still have access to the important things: my old code and the knowledge I've gained from writing that code.

Second, new code presents an opportunity for a more correct solution. So you've sat down and written a lot of bad code. You feel like deleting it would mean deleting the progress you've made towards a correct solution. But surely you haven't been asleep this whole time? Your mind has been watching the code you're writing and doing its own work - pondering the patterns behind the madness, taking note of vulnerabilities, seeing patterns that could become functions, etc. You've gained a lot of experience just by doing it wrong and at the very least you have a better idea of what NOT to do once you start over. A fresh start allows for a more pure expression of the solution that can't be shoehorned into your ill-conceived beginning.

Third, you will improve your tests by trying things different ways. If you use a FOR loop then you won't encounter errors that you might if you used a WHILE loop. If you're using dynamic memory allocation instead of static you have more things to worry about. Each new method of solving the problem before you opens up new areas for failure. Will this sub-function I just started using return 0x00 for any reason? Because I return its value without checking it and zero would not work well for a return value. So write a unit test to ensure that the return value is never 0. That's one more level of certainty that you're writing correct code, not just code.

Fourth, writing new code allows you to follow best practices more closely. So after a few hours of hacking together your test program-turned release candidate you decide to see what happens when you run splint on it. Turns out it produces so many warnings that your ego crawls into a hole and hides. What's the right course of action? Fix the warnings one by one until the splint output is clean. What do most people do? Turn off splint and wait for it all to be caught in integration. It's much easier to run splint on a 5-line function than 1000. Starting over gives you the chance to comment, run analysis tools, develop unit tests and format your masterpiece when it's a much more manageable size. This will only help you in the long run by front-loading much of the work that leads to clean, well-running and tested code at integration time.

Whenever you are writing - be it code, a short story, a joke, a play, a manual on annoying your cat remember this: the measure of how well you have written isn't the number of words - it's whether you accomplished what you wanted with those words. Eliminating incorrect code instead of massaging it back to life costs you nothing, lets you write more correct code, expands the range of your unit tests and helps you follow process more closely. So don't save that crufty old code, replace it and move on.