So for the past week or two, I've had a design slowly evolving for a Pretty Damn Cool project. I need a reality check on whether I can even attach all this stuff to a single arduino and have a shot in hell at any reasonable speed. I know I can physically connect it all. Hell, I have pins left over. This is also my first arduino project, but not my first time on a uC. It is, however, my first time connecting up the hardware.
I'm an amateur race car driver on a very ambitious team competing in a highly competitive endurance racing series. My team's primary financial support comes from the generosity of an Internet community, and we try to keep them sated with boatloads of pictures, in-car video, the works. Our effectiveness there is kind of limited because many race tracks aren't known for having readily available electricity and indoor plumbing, much less Internet connections or cell coverage.
Anyway, this planned device serves four purposes: 1) It collects handy statistics and Science(TM) that would make for pretty graphs to share with our supporters. 2) It collects important data that allows us to fine-tune the performance of the car and monitor for impending failures in real-time so we can pit BEFORE it blows up or catches fire 3) It resolves some nagging communications issues (I'll explain this more later) 4) It gives us a neato piece of kit that we might sell a few copies of to our competition. Naturally they could always build it themselves from schematics and source, but just because you can weld doesn't mean you can solder.
So, starting with the Arduino Mega2560, I'll be adding: 1) An independent 5v, 3v3 and 12vDC/DC converter (1 amp each) - 12v driving the VIN pin, 5v and 3.3v supplied as necessary to all the other ICs and boards 2) An ELM327 and all its associated circuits to talk to all the various flavors of OBD2 computer. This outputs to hardware serial 2. (Serial 0 is reserved for debugging) 3) Hardware serial 1 and some power to an RJ45, to which any of a bazillion GPS modules could be connected. The module itself will live in a weatherproof box on the roof. 4) I2C to another RJ45. This RJ45 carries both 5v and 3v signal levels (shifted by a MAX3372) along with both flavors of power, a ground, and an interrupt pin. Planned for connection to this bus are breakout boards for:
- Two ADXL345 3-axis accelerometers, one mounted low and one mounted high
- Two ITG-3200 3-axis gyroscopes, again one mounted low and one high
- Roughly a half dozen MLX90614 IR temperature sensors pointed at various things - brake discs, tires, engine parts
- Possibly some EEPROMs if I run out of configuration space on the internal one. These will be on-board, however.
5) Broke out a 1-wire pin to an as-yet-undetermined connector. This will connect to more mundane temperature probes covering things like air temperatures. 6) An HD44780-based LCD, again with an RJ45. They're damned handy connectors. This will be part of a modification of the HUD system our car came from the factory with to make it actually include all the relevant information. 7) A radio comms subsystem. This one bears some explaining.
Communications between the pit and a driver are extremely dicey when you're trying to do it on a budget and without FCC licenses. Oddly enough, FRS/GPRS radios seem to work better than more expensive systems anyway. The driver's helmet is wired with a mic and earpiece, and a PTT button is on the steering wheel. We'd like the arduino to be able to talk to a computer back at the pit to notify us when certain alarm conditions are reached, and for those of us in the pit to be able to have our computer tell the arduino to flip on a light telling the driver to pit in. Just like Formula One! At first, this seemed stone cold impossible using FRS/GPRS - and then I happened to use a landline phone. DTMF tones! You can even encode hex in them!
So this subsystem involves a digital pin driving the PTT (in parallel with the driver's button), a Holtek HT9200B DTMF tone generator connected to the microphone line and an MT8870 DTMF decoder connected to the earpiece line. Naturally there will have to be some protocol work here to handle retries and prevent multiple cars from responding to one another's signal, but that's fairly easily worked out.
8) An on-screen display style video overlay produced by an MAX7456 on SPI. Video in, video out. Pretty simple. It'll get speed, acceleration, and position data. Maybe lap count and driver name or something like that.
9) An SD card for logging all this data. I'd like SDHC and FAT32 compatability, so I've decided not to do it directly in the Arduino - so a second atmega32 will be running this: http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html I may keep his serial interface, or I may convert it to SPI since those pins are waaaay closer to where it physically fits on the board.
10) A breakout header for 5 LEDs (with onboard resistors) to be used for indicators like 'pit now you idiot' and 'GREEN FLAG GOGOGOGO'
11) A breakout header for 5 driver-actuated switches/buttons meaning to tell the system to send out DTMF tones for 'I'm pitting' or 'I crashed' or 'I got a penalty' or whatever.
12) An RTC, preferably shared between the arduino and the other atmega.
All of this on a through-hole (except a few ICs that don't come in through-hole format) oversized shield under 5x5 inches. Through hole because I suck at soldering. It'll all be breadboarded as individual subsystems first, of course - the last thing I need is to drop $90 on a junk PCB :P
Here's some psuedocode. If I can achieve ~60 loops per second I'll be happy:
loop()
{
lastSensorTime = millis()
getGPSData() //serial
checkSwitchStates() //direct digital pins
get1WireSensorStates() //OneWire library.
getStateFromI2CSensorsWithoutInterrupts() //This only gets from the non-interrupt-capable sensors for speed. The MLX supposedly doesn't work with the stock I2C lib, so I'm not sure what I'm going to do here
pollOBDForSensorData() //serial
updateOutputs()
}
updateOutputs()
{
updateLCD() // LiquidCrystal. 4-bit mode on the LCD controller
if(alarmStateReached)
{
setDashLEDs() // via direct digital pins
sendDTMFTones() //via direct digital pins
}
logToSD () //fast serial
if(debug) logToSerial() //Spit out everything that goes into the log to Serial 0.
}
onVsync() //Interrupt generated by the onscreen display chip indicating it's safe to change text without
{
if(lastSensorTime > lastOSDRefresh)
{
refreshOSD() // Over SPI
}
lastOSDRefresh = millis()
}
onI2CInterrupt() //From shared interrupt line from the I2C sensors
{
//Maybe rate-limit this.
lastSensorTime = millis()
getSensorStateFromInterruptableI2C()
updateOutputs()
}
onDTMFTone() //Interrupt generated by DTMF decoder when it hears a valid tone
{
if(millis() - lastDTMFTime > timeout) clearDTMFBuffer()
lastDTMFTime = millis()
addToDTMFBuffer() //Direct digital pins.
evaluateDTMFBuffer()
}
evaluateDTMFBuffer()
{
if(DTMF buffer contains a valid command for this car)
{
decodeDTMFBuffer()
updateOutputs()
clearDTMFBuffer()
}
}