So I bought the device mentioned and I'm having difficulty getting it to work. I've got the DB9 to OBDII cable and I'm testing with a 2008 Subaru Legacy.
I'm using the manufacturer's Demo code (see attached) with a few modifications; since I'm not using the LCD screen, I have modified the lines in the Loop() to say "Serial.println" instead of "sLCD.print" so that I can see the values.
What happens is this- I see the intro line, which says "ECU Reader"; I then see 4 lines which instruct you on which button to hit. When I hit "Down", I see "CAN", then "CAN Init OK", and then I will see anywhere from 1 to 6 lines containing the actual values before the stream simply locks up and doesn't print anything else. The values are accurate because I'm watching the gauge cluster as I run it.
I have tried changing the Serial baud rate to 115200, increasing "buffer[]" to 1024 bits (code won't run), taking a random crack at changing the CAN bus speed to 1Mbps (Legacy is on 500Kbps, so this also didn't work), changing the Loop() delay to both 10 msec and 1 sec, and finally commenting out any operation besides printing the CAN bus values to the screen. Nothing seems to change my initial issue.
There is a lot of code to look at and I am only a Novice anyway. I will take a wild stab at it and say that you need to try taking out the GPS part of the code, and delays and see if that helps. I think maybe that you are getting over ran by the GPS serial.
I don't know, though, because if I hit "DOWN" the loop should only call the polling requests and send to Serial, not run any of the other functionality (i.e. GPS, logging, SD Card test).
Your code is around 600 lines and is a bit hard to find an obvious problem. I have been working on an OBD2 style code myself and like your code it is getting bigger and bigger with every addition of a new function.
What has worked the best for me is working out one piece at a time. For instance start with the CAN communication, capture the data that is returned from your car and print it to the serial monitor. When you see that you data looks reliable and correct set that code aside. Next, start the logging portion of the code by storing your time stamp or whatever your first saved line that you want to have on your card. When you are ready get the GPS to work by itself. etc.
When each part of the code is functional and reliable, add one more piece and test your new code to make sure it still works.
That's the thing, though- I'm only testing what's in the Loop() method right now. The Setup() method determines whether or not the other functions will be called depending on which button is pressed, and I'm only pressing "Down".
The issue here is that the Loop() method only runs a handful of times and then locks up, and the Reset button must be hit to bring it back online. The problem here is whatever is happening in those 30 or so lines of code in the Loop() method.
Which Arduino are you using? If it is a 328-based Arduino, it has 2048 bytes of SRAM.
#include <SD.h> /* Library from Adafruit.com */
There goes 512 bytes or more.
char buffer[512]; //Data will be temporarily stored to this buffer before being written to the file
char tempbuf[15];
char lat_str[14];
char lon_str[14];
Another 555 bytes gone.
With all the Serial.print() statements, and serial buffers, etc., you are are almost certainly running out of memory. There are a couple of available memory functions that you can add to the sketch, and call at appropriate points, to determine whether or not you really are out of memory.
I wanted to ask something for some time now...
Can someone, with that shield and that cable, not only read from the brain of the car, but also program and change settings to the brain of the car?
Paul- good call on that, I'll check the version when I get home. If I find that's the case, what are the memory functions you mentioned? And what would be a more appropriate way to see what the device is doing live than a bunch of Serial.Print() statements?
Nik- the answer is sometimes, maybe. I say that because the CAN standard really only defines bus communication, not how the vehicle's ECU responds to requests or how they store information. Sometimes even figuring out the correct polling responses is different car-to-car, and then there is the question of whether the OEM allows for map or table changes on the bus. Sometimes, you have to directly connect to the EEPROM in the car.
The ECU may not provide the level of functionality you're looking for, either. This is why racers and tuners use replacement ECUs. If you're serious about going that route, Motec makes an ECU called the M1 in which you actually write a good portion of the firmware yourself, in a C-based language.
And what would be a more appropriate way to see what the device is doing live than a bunch of Serial.Print() statements?
None, really, but you can move the constant strings out of SRAM, using the F() macro.
Serial.print(F("This string doesn't occupy any SRAM"));
Serial.print("This one does");
nikolakos:
I wanted to ask something for some time now...
Can someone, with that shield and that cable, not only read from the brain of the car, but also program and change settings to the brain of the car?
If it's possible I fairly sure you don't want to be playing around with this ability, at least not with any automobile you plan on keeping reasonably functional. Also, a major mistake could "brick" one or more of the automobile's microcontrollers, which for most people would mean an expensive trip to a dealership/professional mechanic familar with automotive electronics systems for repairs. In other words, don't try this at home unless you actually know what you're doing!
The board I'm using is the Arduino Uno, which uses the ATMega328. Therefore, I am dealing with that 2KB of SRAM.
When I use the F() macro like so...Serial.print(F(buffer));...I get an error that reads...error: initializer fails to determine size of '__c'
I'm looking around on the Net for solutions now.
I noticed on another forum someone mentioning that they included a terminating resistor on the shield side of the CAN bus; anyone think that might also be a problem?
pgm575:
I noticed on another forum someone mentioning that they included a terminating resistor on the shield side of the CAN bus; anyone think that might also be a problem?
You should never have to do that. There should only be 2 terminating resistors, one at each end of the CAN bus, which should already be on the car. Every branch which splits off from the main CAN bus is allowed 1 meter to be within specifications. Things will work outside of specification as i've done way longer than 1 meter before, but there are no garuntee's it'll work.
After a little hiatus to get some other things done, I got back to this project and switched cars. Now I'm testing on a 2005 Ford Focus.
With the Ford, I'm getting a different result- now I seem to be making it through the loop as many times as I want but I'm getting no response from the CAN bus. I added a quick status line to tell me when I was back around, like so-
void loop() {
Serial.println(F("Made it to loop."));
if(Canbus.ecu_req(ENGINE_RPM,buffer) == 1)
...etc etc. When I run the code now, all I see in Serial Monitor is "Made it to loop" repeating over and over again.
I've tried initializing the CAN controller at 125, 250, and 500 Kbps just for good measure, always with the same result.
I suppose at this point I'll have to start digging into the 2005 version of the CAN specification and the "Canbus.h" file but if anyone saw a pattern linking the previous issue to the current issue, it would be much appreciated.
A little more research...the '05 Focus is likely to be using J1850 PWM, rather than the ISO 15765 the '08 Legacy is likely to be using. This means a bus rate of 41.6Kbps versus 500Kbps and bus pins of 2/10 versus 6/14. Therefore, my current setup won't communicate with the Ford.
Tested on a 2008 Nissan Frontier and the CAN comms worked flawlessly. Looks like I probably ran into some arbitration issues with the '08 Legacy but I'll leave that alone for now.
So now that the comms are working, I wanted to try the SD Card logging function. The headers and object instantiation go like...
Serial.println("Init SD card");
delay(500);
//clear_lcd();
Serial.println("Press J/S click");
//sLCD.write(COMMAND);
//sLCD.write(LINE1); /* Move LCD cursor to line 1 */
Serial.println("to Stop");
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance.
if (!card.init(SPI_HALF_SPEED,9)) error("card.init failed");
// initialize a FAT volume
if (!volume.init(&card)) error("volume.init failed");
// open the root directory
if (!root.openRoot(&volume)) error("openRoot failed");
// create a new file
char name[] = "WRITE00.TXT";
for (uint8_t i = 0; i < 100; i++) {
name[5] = i/10 + '0';
name[6] = i%10 + '0';
if (file.open(&root, name, O_CREAT | O_EXCL | O_WRITE)) break;
}
if (!file.isOpen()) error ("file.create");
Serial.print("Writing to: ");
Serial.println(name);
// write header
file.writeError = 0;
file.print("READY....");
file.println();
The problem is that the function freezes at "file.writeError" and doesn't print anything past that. I even tried putting in my own Serial.PrintLn statements before and after this line and nothing showed. This is interesting because I see "Writing to: " and the name of the file get printed to the Serial Monitor, but then it basically stops working at the "write header" comment.