LT1 Swapped Porsche - Gauge Functionality

I think the first thing I need to do is set this code up not as a pass through for all messages, but rather to filter out certain messages. It seems all the good stuff I need (tach, oil pressure, coolant, idiot lights, etc) is on 4 or so frames from the GM ECU, and received by the same number on the Porsche side. I’m trying to find examples of people using the due_can setup to better understand the functionality or how to properly set it up.

So what I did was setup a filter.

//By default there are 7 mailboxes for each device that are RX boxes

//This sets up filtering to look for the messages needed to run a gauge cluster Can0 is the Engine side, Can1 is the Porsche side
int filter;
//extended
for (filter = 0; filter < 3; filter++) {
Can0.setRXFilter(filter, 0xFF, 0, true);

//At this time, no messages going from Porsche to GM so comment out
//Can1.setRXFilter(filter, 0, 0, true);
}

//If Standard frames, enter below
//standard
//for (int filter = 3; filter < 7; filter++) {
//Can0.setRXFilter(filter, 0, 0, false);
//Can1.setRXFilter(filter, 0, 0, false);

So now only frame FF is passing through. I have nothing going from Porsche to GM right now, I think I will end up passing a few items though in the end (vehicle speed, sport mode? - it would be sweet to get active rev matching functional).

The later section of code I think should be setup differently now. No need for an if statement I guess.

CAN_FRAME incoming;

if (Can0.available() > 0) {
Can0.read(incoming);
if (incoming.id == 0xFF) {
printFrame(incoming);
RPM = incoming.data.bytes[1]};
}
myFrame.id = 0x105;
myFrame.length = 8
myFrame.data[2] = RPM;
Can1.sendFrame(myFrame);
}

Sorry, I’m very new to messing with Arduino. My job (calibrator for one of the Big Three) has me reading lots of code, never writing.

Also, as I look at my DBC files, I realize engine speed is on two frames in both. So I need to set that up. I believe one is little endian and one is big endian. That stuff I can sort out later.

I have my M2 now. I made a code that sends on CAN1 some random numbers to run the tach and it works! CAN0 I have setup to monitor CAN1bus as well to verify my message goes out. Next step will be getting RPM from the engine to my outgoing signal.

Two questions… is there something I am doing wrong for power. I have the Under the Hood version. I have 12V to pin23 and ground to Pin 12. I only get the board to power on if I have USB plugged in… Are there more spots that need power/ground?
EDIT: Figured this one out. Ground needs to go to the digital ground (any of them?) as well.

Using due_can, to write to certain bytes, I can use, "myFrame.data.byte[1], can I do something similar with individual bits? For example, in my case, the first 7 bits of byte one send accelerator pedal. Bit 8 is an engine running flag.

Man, this is a great project @superman22x! I’d love to see a video of your tach moving or whatever else you want to show off.

Regarding powering UTD - I think you’ve got it. Pin 23V gets 12V and and of the DGND pins get GND.

https://docs.macchina.cc/m2-docs/detailed-reference/installation#under-the-hood-uth

@CollinK - can you point us at any examples that shows changing/sending individual bits?

Sure! I’ll try and grab something in the next couple days! Hoping to take it for a drive soon.

I found a way to manipulate individual bits by using bitSet(x,n). But for example one signal starts at bit 0 Byte 0 and is 10 bits long. Or another starts on Bit 7 of byte 5 and is 8 bits long. It seems odd, but I’m pretty certain on these…

If you’re really up to date with due_can and can_common then there is built-in bit manipulation. If you have a CAN_FRAME object like so:

CAN_FRAME myFrame;

Then you can do this:

myFrame.bit[3] = 1;

But, that’s rather tedious when you need to set, say, 10 bits. Let’s say you know that RPM is stored across 12 bits, all 8 in the first byte (so 0-7) and the lower 4 of the next byte in the frame (8-12). This would then be spread across myFrame.data.byte[0] and myFrame.data.byte[1]. What if other stuff is stored in the upper 4 bits of that second byte, byte[1]? You could do something like this:

uint16_t RPM = 2000;
CAN_FRAME frame;
Can0.read(frame);
frame.data.byte[0] = RPM & 0xFF; //lower 8 bits of the RPM
frame.data.byte[1] = (frame.data.byte[1] & 0xF0) | ( (RPM >> 8) & 0xF );
Can1.sendFrame(frame);

Now, that includes a rather nasty line. Here it is broken down with comments:

frame.data.byte[1] = frame.data.byte[1] & 0xF0; //zero out the bottom 4 bits but leave the top 4 alone
uint8_t valu = RPM >> 8; //shift RPM down 8 bits so we're left with just the top 8 but shifted down to where the lower 8 used to be
valu = valu & 0xF; //keep only the lower 4 bits of those 8, zeroes out the upper 4
frame.data.byte[1] = frame.data.byte[1] | valu; //form a composite of the existing upper 4 bits plus the new lower 4 bits from valu

So, that’s basically how you compose a series of bits that fall within two or more bytes in the message. Is it confusing? Very much so. Believe me, it took me a lot of work to create the DBC code in SavvyCAN because of how confusing this all is. It gets easier with time. You just have to take it slowly and work through it. Basically the idea is to use & which is “AND” to mask away bits you are about to change. Then use | which is “OR” to compose new bits into those positions.

This is extremely helpful, thank you! I think I can figure it out with some testing. Takes me a few tries usually, then it clicks

I’m a little stuck on something… Trying to use what you showed to do this. I have a value, torque, that comes in on all of Byte[3] and the lower of byte[4]. So in total it’s a 12 bit number. I then need to multiply and offset it to get it to the correct scale for the Porsche output. Once I have that final number, I send it out as only an 8 bit message.

I keep getting Low and High bits mixed up. But here is my example, am I going in the write direction here?

EngTorqL=incoming.data.bytes[3];
EngTorqH=incoming.data.bytes[4];

EngTorqH = EngTorqH & 0xF0;
EngTorqH = EngTorqH >> 4;
EngTorq=EngTorqL | EngTorqH ; //EngTorq assigned as int earlier

EDIT: I may have figured out… something? It seems to work. The above did not. I made EngTorq a uint16_t as well. Does changing EngTorqL from a byte to uint16_t cause problems?

EngTorq = ((uint16_t)EngTorqL << 4) | ((EngTorqH & 0xF0) >> 4);

Couple updates, made a nice box for the M2. Used an Amphenol connector… because why not. I’m a little afraid the stock M2 24 and 26 pin connector will not last long, we’ll see…

My CAN system works great, I have full gauge functionality now. I may dig further to see about getting traction control working.

I used the 12VIO system to trigger my electric powersteering pump as well. For now, it’s set to go as soon as the M2 turns on, but I will implement a trigger based on engine running next.

The next hurdle however, is that the M2 boots too slow relative to the rest of the car, so warning lights already trigger and the fans go into max run before the M2 starts sending out CAN messages. I have the M2 set to trigger on key on, but I think I may need to find another solution. Maybe door opening? Or maybe running all the time… I would just be afraid to kill the battery if it’s parked over a few weeks or more. Porsche has that left side ignition for Le Mans starts, so even with door open triggering it, I need to be careful! Haha.


Very cool. Glad to hear it is working! Any chance you could take a little video? I’m very curious.

As far as triggers I know we have done a few projects where the hardware comes out of sleep mode on CAN activity. Like unlocking the doors as you approach the car. I’m not sure if we have done this with M2 specifically though.

I wonder if a wakeup scenario would be fast enough vs a full boot cycle. It looks like a couple systems wake up when the key is inserted, so if I can trigger it off that, that would be ideal. Sleep could be setup to trigger based on not receiving a CAN message from either Porsche or GM side in X amount of time.

I’ll try and grab a quick video soon.

Stuck on using the sleep functions… It seems only the analog pins are mapped to the connectors on the M2 board. I believe only digital pins can be used as an interrupt function on the Due if I were to use a sleep mode. Any other ideas here?

I found some old threads that started working on low power modes, but I couldn’t find any completed project info… I am at around 110mA with my M2 now. Way too high to leave it idle. From applying power to turning on the LED from my setup code, it looks like 2-3seconds. If anyone has any solutions, I would be very interested.

One idea I have is to use an Arduino Nano or similar minimal board to trigger a relay. It seems the Nano uses less than 1mA in sleep mode. I have a trigger wire that is used to “Wake” the radio, instrument cluster, etc when the door is opened. I tried to use this to power the M2 directly, but it is not meant for that. It might work to power a very low powered coil instead of a trigger on something like the Nano.

Interesting new development today… For the time being, I switched the M2 back to a constant 12V source (I keep my car on a tender now anyway, and it likely won’t be driven anywhere until spring). When the car is off, the 12V IO system is on. As soon as I key on, the 12V IO system turns off. The 12V input drops from ~12.3V to 12.0V when the key is turned (no crank) as some other systems in the car put some demand on the system. For whatever reason, my car is triggering something to shut the 12V IO system off. Does a slight voltage drop do that? I’m going to dig into that.

This stems back to my other thread on getting the 12V IO system to work.

I flashed the M2_IO example script to see what it would do. It appears my M2 12V IO is shutting down due to an over current trip. This only happens if it is plugged into the vehicles 12V source. Also, 76mA shouldn’t be triggering an over current? I’m confused on this. It happens when the key is put to the acc mode, or any other load change on the battery. Just to confirm I wasn’t doing something else dumb, I powered the M2 with a 9V battery and removed only the vehicle power source from the equation. Problem gone.

In addition, I had the same issue before when the 26 pin connector was not connected at all. I’m assuming the slight change in vehicle voltage is somehow causing issues with the overcurrent detection.

OverCurrent

Here’s a video I’ve been promising. Apparently my oil pressure gauge isn’t working again… I must have goobered something when I “cleaned” my code up. The PSM failure etc goes away when I drive, it comes up if the battery gets disconnected.

I’ve been working on power solutions still. I found a 12V trigger that turns on when the door opens, but it doesn’t power the M2. I’m thinking a mosfet triggered by the 12V source is the way to go.

Very cool! I will ask around on what low power/fast boot options you might have.

Thanks! I made a thread on the fast boot stuff. I figured that might be of interest to others, so I thought a standalone thread would be good.

1 Like

I see that, good idea. Let me know if you want some of your older posts moved over there.

So for now I have a good alternative on power. Since my wake up signal is 12V, and I need a full 12V to the M2, I decided to use a pair of MOSFETs like the below diagram as suggested on the Arduino forum. It worked great! I’ve never used Mosfets before, but I’m a fan now, simple. Wakeup triggers off of door opening. That turns off if you were to not insert the key within a short time. But it retriggers off of the key being inserted, and stays on once the car is on.

I finally made a GitHub posting. I changed my code to simply use the OBD PIDs to function, so that I can share it without any worries of sharing GM CANbus. I’m pretty excited to be able to share this, it’s enough to get the basics working! Unfortunately, I don’t think Oil pressure or Oil Temp come across the OBD PIDs.

Awesome work! We are going to feature this in the next newsletter.

So I’ve been refining bits and pieces with the code. Couple big problems I still have:

  • Boot time - if I open the door and key on, it’s ok, but if I left the door open for any measurable amount of time, I have to insert the key, count to 3, then turn it. If I don’t do this, I set a series of messages.

  • 12VIO current monitoring does not play at all with a car’s fluctuating voltage system? I have it set to re-enable the instant it trips in my code, and if I were to print that to my serial monitor, it’s constantly tripping on very small currents. Not good.

  • 12VIO seems to take significant resources to turn on/off a pin? For example, if vehicle speed is above 0.5kph, I am changing the frequency on the output pwm pin. But if speed drops below that, I have the 12VIO turn that pin off. When that happens, my CAN messages all dropout for 500ms or so, which lights up the dash.

If anyone has any thoughts on these things, it would be greatly appreciated!