When a program reaches memory limits, expect that it will grow further. Then it's a waste of time to tweak specific parts of the code, because much more time will be required to maintain such code later.
Like I said, I've only implemented one third, if that, of the functions that I want my CarDuino to have, and already that sketch claims three quarters of the 328's resources. So far, it does little more than poll two other ICs in regular time intervals via I2C and display a few numbers on the screen with a few icons (which are stored as BMPs on the microSD card).
I have yet to implement things such as a light bulb checking device (which will mean two more Attinies to poll via I2C), as well as a "convenience module" which will monitor outside temperature, windshield wash level, and if I can get myself to really go crazy, it will also monitor if the soft top is properly shut or thrown all the way back (this is a convertible). So that means probably another Atmega328 to poll, and puts my total number of I2C devices within my CarDuino network at a whopping six.
All this means additional variables that will have to be handled. Moreover, I still haven't really designed a user interface that lets you navigate back and forth between different pages of the TFT screen, as well as a "preferences" page, where I want to be able to adjust user settings that will then be stored in the EEPROM.
Again, I know that this is a huge project, it's probably going to be one of the bigger programming jobs I have ever done, including much of the work I have done with databases and web pages. And although progress has been really quite considerable in a short amount of time, which also gives me confidence that this kind of project can be done in the end, I don't expect this "mother of all CarDuinos" to be ready to install before next spring.
3) Stay away from direct port access, or similar low-level coding. It will bite you when you have to debug or modify your code later.
So far, I'm only doing direct port access with an Attiny that interprets the car's crankshaft sensor signal and generates an RPM reading from that. The sensor produces a very fast moving square wave, with cycle lengths of about 200 us when the car is revved up to high RPMs, and because digitalRead takes up precious microseconds, I am directly accessing the port with my pin change ISR to check if a pin is high or low. But because that's almost literally the only thing that this Attiny does, besides calculate the true RPM from the signal and report it as a 16-bit integer to the head unit Atmega via I2C, I don't expect difficulties. The code on that Attiny is about 96% finished and so far there haven't been any problems.
Here's the key part of that code:
GIMSK = 0b00100000; // pin change interrupt
PCMSK = 0b00000010; // interrupt on pin PB1
sei(); // enable interrupts
if ((PINB & 1 << crankshaft) != 0) crankRisingTimestamp = micros();