MEGAsquirt A place to collectively sort out this megasquirt gizmo

Building my own digital WBO2-CAN Adapter - help?

Thread Tools
 
Search this Thread
 
Old 06-17-2016 | 01:32 PM
  #1  
Morello's Avatar
Thread Starter
Junior Member
 
Joined: Jul 2009
Posts: 419
Total Cats: 45
From: Orange County, CA
Default Building my own digital WBO2-CAN Adapter - help?

Because I like to tinker and it doesn't seem all that complicated, and I want smooth closed loop fueling goodness, I've embarked on building my own adapter to take the LC2 digital output and feed it to my MS3 basic over CAN. I am using a Teensy 3.1 micro controller (think arduino on steroids, has a built in CAN controller), and I have a max3232 serial to UART bridge. However I cannot seem to read any messages coming from the LC2 on the Teensy, through the max3232. Does anyone know if it follows normal RS232 protocol? I can read LC2 messages on my laptop in realterm, and I can send messages at the same baud rate to my teensy from realterm. But I can't read messages on the teensy from the LC2.

In summary, this works:
realterm (set to 19200,8n1) --> usb/rs232 adapter -> max3232 -> teensy --> serial monitor

and this works:
serial monitor --> teensy --> max3232 --> usb/rs232 adapter --> realterm (set to 19200, 8n1)

and this works:
wideband lc2 --> usb/rs232 adapter -> realterm (set to 19200, 8n1)

but this doesn't:
wideband lc2 --> max3232 --> teensy --> serial monitor
I'm not getting any messages whatsoever on the Teensy, not even some garbled mess suggesting that it's a translation issue. The only thing I can think of is that the LC2 doesn't actually use RS232. But I don't have a scope to check the TX voltage (I don't want to fry my microcontroller)

Hoping someone who's embarked on something similar can shed some light. Thanks!
Old 06-17-2016 | 01:35 PM
  #2  
Morello's Avatar
Thread Starter
Junior Member
 
Joined: Jul 2009
Posts: 419
Total Cats: 45
From: Orange County, CA
Default

Code is simple. Read from one, send to other. Serial is the serial monitor on my laptop. Serial1 is wired into the MAX3232.
Code:
#define HWSERIAL Serial1


void setup() {
Serial.begin(9600);
HWSERIAL.begin(19200, SERIAL_8N1);
}

void loop() {
int incomingByte;

if (Serial.available() > 0) {
incomingByte = Serial.read();
Serial.print("USB received: ");
Serial.println(incomingByte, DEC);
//HWSERIAL.print("USB received:");
//HWSERIAL.println(incomingByte, DEC);
}
if (HWSERIAL.available() > 0) {
incomingByte = HWSERIAL.read();
Serial.print("UART received: ");
Serial.println(incomingByte, DEC);
//HWSERIAL.print("UART received:");
//HWSERIAL.println(incomingByte, DEC);
}
}
Old 06-17-2016 | 07:56 PM
  #3  
cyotani's Avatar
Senior Member
iTrader: (8)
 
Joined: Jan 2012
Posts: 1,407
Total Cats: 116
From: Azusa, CA
Default

I've attempted doing my own LC2 display using serial communication with the intent of eventually output the data to the MS3 via CANbus. I was able to read the LC2 serial data into an Arduino. However, I was not able to decipher the data. It did not seem to match their documentation at all. I eventually just gave up.


There was another thread of someone else working on a similar project but I can't find it at the moment.
Old 06-17-2016 | 08:04 PM
  #4  
Morello's Avatar
Thread Starter
Junior Member
 
Joined: Jul 2009
Posts: 419
Total Cats: 45
From: Orange County, CA
Default

Originally Posted by cyotani
I've attempted doing my own LC2 display using serial communication with the intent of eventually output the data to the MS3 via CANbus. I was able to read the LC2 serial data into an Arduino. However, I was not able to decipher the data. It did not seem to match their documentation at all. I eventually just gave up.


There was another thread of someone else working on a similar project but I can't find it at the moment.
Do you recall what your hardware setup was to get it communicating right? I was able to decipher the packets manually on my laptop (dumping the serial commands into a csv)
Old 06-17-2016 | 08:23 PM
  #5  
codrus's Avatar
Elite Member
 
Joined: Mar 2007
Posts: 5,187
Total Cats: 859
From: Santa Clara, CA
Default

I wouldn't be surprised if the LC2 is outputting TTL levels instead of RS232 ones -- do you know if the 3232 will accept those?

I'm using Reverant's LC2-to-CANbus adapter in my car, I'll see if I can stick the scope on it and capture the data.

--Ian
Old 06-17-2016 | 08:36 PM
  #6  
Morello's Avatar
Thread Starter
Junior Member
 
Joined: Jul 2009
Posts: 419
Total Cats: 45
From: Orange County, CA
Default

Originally Posted by codrus
I wouldn't be surprised if the LC2 is outputting TTL levels instead of RS232 ones -- do you know if the 3232 will accept those?

I'm using Reverant's LC2-to-CANbus adapter in my car, I'll see if I can stick the scope on it and capture the data.

--Ian
I've read the data sheet and I'm not able to determine, but I imagine not since it is strictly an rs232 transceiver (would expect a negative voltage for logic zero).

I'd appreciate that. Like I said, I could just plug it into my Teensy but if I'm wrong and the lc2 is spitting out over 5v then it's toast.
Old 06-21-2016 | 10:40 PM
  #7  
stefanst's Avatar
Senior Member
iTrader: (1)
 
Joined: Sep 2011
Posts: 1,215
Total Cats: 74
From: Lambertville, NJ
Default

I'm pretty sure I had an LC-1 hooked up straight to an Arduino using TTL-Level and reading the output. Was a few years ago though and memory is weak...
Old 06-22-2016 | 11:14 AM
  #8  
Morello's Avatar
Thread Starter
Junior Member
 
Joined: Jul 2009
Posts: 419
Total Cats: 45
From: Orange County, CA
Default

Got it working. It looks like the male to male db9 connector I used was not properly mapping the pins out to the other side. Hooking them up manually got me going. Successfully reading AFR values, as well as the LC-2 status (warming up, errors, etc).

So for the record, it is RS232 and you can read it with a MAX3232. Next step is hooking up the CAN side and trying to read/send messages. Woop.
Old 06-22-2016 | 07:50 PM
  #9  
Morello's Avatar
Thread Starter
Junior Member
 
Joined: Jul 2009
Posts: 419
Total Cats: 45
From: Orange County, CA
Default

I got it working - my male to male serial adapter was screwing things up. Connected the TX and gnd db9 pins directly and now it's good. And the code that I found/modified to interpret the lc1 packets works as well - reads the status packet accurately and shows the afr. Next step is to get it CAN communications going.
Old 06-25-2016 | 03:46 AM
  #10  
gooflophaze's Avatar
Senior Member
 
Joined: May 2007
Posts: 997
Total Cats: 156
From: Atlanta
Default

these snippets might help.. fair warning, I haven't touched this code in a little over 2 years.

Code:
void LC1::get_AFR() {
  byte incoming_byte;
  byte index;
  byte LC1_status;
  byte LC1_AFR;
  byte LC1_AFRs;
  int LC1_lambda;
  byte packet[8];     

  while (Serial3.available() > 0){
    incoming_byte=Serial3.read(); 
    if ( (incoming_byte & 0xA2) == 0xA2 ) { // synchronization byte
      index=0;
      packet[0]=incoming_byte;
    } 
    else if ((index == 0) && ((incoming_byte & 0x80) == 0x80)) { // 2nd syncro byte
      index=1;
      packet[1]=incoming_byte;
    } 
    else {
      index++;
      packet[index]=incoming_byte;
    }
    if (index >= 5 ) {
      LC1_status = (packet[2] & B00011100) >> 2;

      LC1_AFRs = (packet[2] & B00000001) << 7;
      LC1_AFRs += packet[3] & B01111111;

      LC1_lambda = packet[4] & B00111111;
      LC1_lambda = LC1_lambda << 7;
      LC1_lambda += (packet[5] & B01111111);

      switch (LC1_status) {
      case 0: 
        LC1_AFR = LC1_lambda; 
        break; // reporting lambda
      case 1: 
        LC1_AFR = ((LC1_lambda + 500) * LC1_AFRs ) / 1000; 
        break;
      case 2: 
        Serial.println("Free Air Calibration in progress..."); 
        break;
      case 3: 
        Serial.println("Need Calibration"); 
        break;
      case 4: 
        Serial.println("Heater Warming"); 
        break;
      case 5: 
        Serial.println("Heater Calibration"); 
        break;
      case 6: 
        Serial.println("Error Code"); 
        break;
      case 7: 
        Serial.println("Reserved"); 
        break;
      }
      if (LC1_status == 0 ) {
        Serial.print("lambda ");
        Serial.println(LC1_lambda);
        Serial.print("AFR ");
        Serial.println(LC1_AFR);
        index=0;
        for (byte i; i <=6 ; i++) packet[i]=0;
      }
    }// end index_5
  }//end while
}// end lc1_read
Code:
void MSRequest(byte block, unsigned int offset, byte req_bytes) {
#define CAN_ID 3
#define MEGASQUIRT_ID 0
  byte databuffer[8];
  enum msg_type {
    MSG_CMD, MSG_REQ, MSG_RSP, MSG_XSUB, MSG_BURN        };

  databuffer[0]=block;
  databuffer[1]=offset >> 3;
  databuffer[2]=(((offset & B00000111) << 5 ) | req_bytes);

  MS_tx_packet(offset, MSG_REQ, CAN_ID, MEGASQUIRT_ID, block, 3, databuffer);
}


void MS_tx_packet(unsigned int var_offset, byte msg_type, byte from_id, byte to_id, byte var_block, byte dlc, byte data[8]) {
  //Spare bits unused
  byte IDR0, IDR1, IDR2, IDR3;

  /* Megasquirt CANBus Format -> FlexCan decoder
   .          76543210
   .   IDR0 - ___ooooo - 3 bits blank, offset
   .   IDR1 - oooooomm - offset, msg_type
   .   IDR2 - mffffttt - msg_type, from_id, to_id
   .   IDR3 - tbbbbBss - to_id, block, spare
   .   Flexcan msg.id
   .            |   IDR0  |   IDR1  |   IDR2  |   IDR3  |   
   .   can.id  - 0001 1111 1111 1111 1111 1111 1111 1111
   .   overlay - ___o oooo oooo oomm mfff fttt tbbb bBss
   */

  // Cut n Paste bits into bytes
  //    ___ooooo - first 3 bits truncated in the can.id long
  IDR0 = lowByte((var_offset >> 6)); // shift right, then paste

  //    oooooomm - offset, msg_type
  IDR1 = (lowByte(var_offset) & B00111111) << 2;
  IDR1 += ((msg_type & B00000110) >> 1);

  //    mffffttt - msg_type, from_id, to_id
  IDR2 = ((msg_type & B00000001) << 7);
  IDR2 += (from_id & B00001111) << 3;
  IDR2 += (to_id &  B00001110) >> 1;
  //    tbbbbBss - to_id, block, spare
  IDR3 = (to_id & B00000001) << 7;
  IDR3 += (var_block & B00001111) << 3;
  IDR3 += (var_block & B00010000) >> 2;

  msg.id=0; // clear data
  // assemble bytes into uint32 for msg.id
  msg.id += IDR0 << 24;
  msg.id += IDR1 << 16;
  msg.id += IDR2 << 8;
  msg.id += IDR3;
  msg.ext = 1; // extended ID set
  msg.len = dlc;

  for ( byte idx=0; idx<8; ++idx ) {
    msg.buf[idx] = data[idx];
  }


#ifdef DEBUG
  Serial.print("var_offset: ");
  Serial.println(var_offset);
  Serial.print("msg_type: ");
  Serial.println(msg_type);
  Serial.print("from_id: ");
  Serial.println(from_id);
  Serial.print("to_id: ");
  Serial.println(to_id);
  Serial.print("var_block: ");
  Serial.println(var_block);
  Serial.print("dlc: ");
  Serial.println(dlc);
  Serial.println("msg.id: ");
  Serial.println(msg.id, HEX);
#endif

  CANbus.write(msg);
}
Old 06-28-2016 | 10:30 PM
  #11  
Morello's Avatar
Thread Starter
Junior Member
 
Joined: Jul 2009
Posts: 419
Total Cats: 45
From: Orange County, CA
Default

Originally Posted by gooflophaze
these snippets might help.. fair warning, I haven't touched this code in a little over 2 years.


Code:
void LC1::get_AFR() {
byte incoming_byte;
byte index;
byte LC1_status;
byte LC1_AFR;
byte LC1_AFRs;
int LC1_lambda;
byte packet[8];

while (Serial3.available() > 0){
incoming_byte=Serial3.read();
if ( (incoming_byte & 0xA2) == 0xA2 ) { // synchronization byte
index=0;
packet[0]=incoming_byte;
}
else if ((index == 0) && ((incoming_byte & 0x80) == 0x80)) { // 2nd syncro byte
index=1;
packet[1]=incoming_byte;
}
else {
index++;
packet[index]=incoming_byte;
}
if (index >= 5 ) {
LC1_status = (packet[2] & B00011100) >> 2;

LC1_AFRs = (packet[2] & B00000001) << 7;
LC1_AFRs += packet[3] & B01111111;

LC1_lambda = packet[4] & B00111111;
LC1_lambda = LC1_lambda << 7;
LC1_lambda += (packet[5] & B01111111);

switch (LC1_status) {
case 0:
LC1_AFR = LC1_lambda;
break; // reporting lambda
case 1:
LC1_AFR = ((LC1_lambda + 500) * LC1_AFRs ) / 1000;
break;
case 2:
Serial.println("Free Air Calibration in progress...");
break;
case 3:
Serial.println("Need Calibration");
break;
case 4:
Serial.println("Heater Warming");
break;
case 5:
Serial.println("Heater Calibration");
break;
case 6:
Serial.println("Error Code");
break;
case 7:
Serial.println("Reserved");
break;
}
if (LC1_status == 0 ) {
Serial.print("lambda ");
Serial.println(LC1_lambda);
Serial.print("AFR ");
Serial.println(LC1_AFR);
index=0;
for (byte i; i <=6 ; i++) packet=0;
}
}// end index_5
}//end while
}// end lc1_read


Code:
void MSRequest(byte block, unsigned int offset, byte req_bytes) {
#define CAN_ID 3
#define MEGASQUIRT_ID 0
byte databuffer[8];
enum msg_type {
MSG_CMD, MSG_REQ, MSG_RSP, MSG_XSUB, MSG_BURN };

databuffer[0]=block;
databuffer[1]=offset >> 3;
databuffer[2]=(((offset & B00000111) << 5 ) | req_bytes);

MS_tx_packet(offset, MSG_REQ, CAN_ID, MEGASQUIRT_ID, block, 3, databuffer);
}


void MS_tx_packet(unsigned int var_offset, byte msg_type, byte from_id, byte to_id, byte var_block, byte dlc, byte data[8]) {
//Spare bits unused
byte IDR0, IDR1, IDR2, IDR3;

/* Megasquirt CANBus Format -> FlexCan decoder
. 76543210
. IDR0 - ___ooooo - 3 bits blank, offset
. IDR1 - oooooomm - offset, msg_type
. IDR2 - mffffttt - msg_type, from_id, to_id
. IDR3 - tbbbbBss - to_id, block, spare
. Flexcan msg.id
. | IDR0 | IDR1 | IDR2 | IDR3 |
. can.id - 0001 1111 1111 1111 1111 1111 1111 1111
. overlay - ___o oooo oooo oomm mfff fttt tbbb bBss
*/

// Cut n Paste bits into bytes
// ___ooooo - first 3 bits truncated in the can.id long
IDR0 = lowByte((var_offset >> 6)); // shift right, then paste

// oooooomm - offset, msg_type
IDR1 = (lowByte(var_offset) & B00111111) << 2;
IDR1 += ((msg_type & B00000110) >> 1);

// mffffttt - msg_type, from_id, to_id
IDR2 = ((msg_type & B00000001) << 7);
IDR2 += (from_id & B00001111) << 3;
IDR2 += (to_id & B00001110) >> 1;
// tbbbbBss - to_id, block, spare
IDR3 = (to_id & B00000001) << 7;
IDR3 += (var_block & B00001111) << 3;
IDR3 += (var_block & B00010000) >> 2;

msg.id=0; // clear data
// assemble bytes into uint32 for msg.id
msg.id += IDR0 << 24;
msg.id += IDR1 << 16;
msg.id += IDR2 << 8;
msg.id += IDR3;
msg.ext = 1; // extended ID set
msg.len = dlc;

for ( byte idx=0; idx<8; ++idx ) {
msg.buf[idx] = data[idx];
}


#ifdef DEBUG
Serial.print("var_offset: ");
Serial.println(var_offset);
Serial.print("msg_type: ");
Serial.println(msg_type);
Serial.print("from_id: ");
Serial.println(from_id);
Serial.print("to_id: ");
Serial.println(to_id);
Serial.print("var_block: ");
Serial.println(var_block);
Serial.print("dlc: ");
Serial.println(dlc);
Serial.println("msg.id: ");
Serial.println(msg.id, HEX);
#endif

CANbus.write(msg);
}
Hey thanks, I think I found your code floating around on the net already. I've definitely used a fair bit of that LC1 code. Got it working this afternoon -

I still need to figure out why the EGO request packets from my megasquirt are reporting 597 and 605 for myvaroffset.
Old 06-29-2016 | 01:18 PM
  #12  
gooflophaze's Avatar
Senior Member
 
Joined: May 2007
Posts: 997
Total Cats: 156
From: Atlanta
Default

MS's request code is kinda stupid (at least it was 2 years ago). It spams the bus. You can write directly to the blockffset if you can find it in the .ini. Values are persistent until overwritten - so you don't have to write the AFR every millisecond, just whenever a new LC1 packet arrives. This is where LC1's averaging/instant settings become interesting.
Old 06-29-2016 | 01:32 PM
  #13  
Morello's Avatar
Thread Starter
Junior Member
 
Joined: Jul 2009
Posts: 419
Total Cats: 45
From: Orange County, CA
Default

Originally Posted by gooflophaze
MS's request code is kinda stupid (at least it was 2 years ago). It spams the bus. You can write directly to the blockffset if you can find it in the .ini. Values are persistent until overwritten - so you don't have to write the AFR every millisecond, just whenever a new LC1 packet arrives. This is where LC1's averaging/instant settings become interesting.
Yeah, that's what I'm doing right now but I'd like to follow the protocol if I can. For some reason the can request for ego data comes in two packets, even though afr has a length of 1. I'm also sending it pretty frequently because I still have the analog gauge hooked up for the time being. If it for some reason craps out and stops sending a signal, then I'll have a backup at least.

I also got the 29bit request/response working to display data on a gauge of some sort. Now to work on the hardware part (which I'm not as good at).

Last edited by Morello; 06-29-2016 at 03:16 PM.




All times are GMT -4. The time now is 10:26 AM.