MEGAsquirt A place to collectively sort out this megasquirt gizmo

Building my own digital WBO2-CAN Adapter - help?

Old 06-17-2016, 12:32 PM
  #1  
Junior Member
Thread Starter
 
Morello's Avatar
 
Join Date: Jul 2009
Location: Orange County, CA
Posts: 418
Total Cats: 45
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!
Morello is offline  
Old 06-17-2016, 12:35 PM
  #2  
Junior Member
Thread Starter
 
Morello's Avatar
 
Join Date: Jul 2009
Location: Orange County, CA
Posts: 418
Total Cats: 45
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);
}
}
Morello is offline  
Old 06-17-2016, 06:56 PM
  #3  
Senior Member
iTrader: (8)
 
cyotani's Avatar
 
Join Date: Jan 2012
Location: Azusa, CA
Posts: 1,407
Total Cats: 116
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.
cyotani is offline  
Old 06-17-2016, 07:04 PM
  #4  
Junior Member
Thread Starter
 
Morello's Avatar
 
Join Date: Jul 2009
Location: Orange County, CA
Posts: 418
Total Cats: 45
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)
Morello is offline  
Old 06-17-2016, 07:23 PM
  #5  
Elite Member
 
codrus's Avatar
 
Join Date: Mar 2007
Location: Santa Clara, CA
Posts: 5,165
Total Cats: 855
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
codrus is offline  
Old 06-17-2016, 07:36 PM
  #6  
Junior Member
Thread Starter
 
Morello's Avatar
 
Join Date: Jul 2009
Location: Orange County, CA
Posts: 418
Total Cats: 45
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.
Morello is offline  
Old 06-21-2016, 09:40 PM
  #7  
Senior Member
iTrader: (1)
 
stefanst's Avatar
 
Join Date: Sep 2011
Location: Lambertville, NJ
Posts: 1,215
Total Cats: 74
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...
stefanst is offline  
Old 06-22-2016, 10:14 AM
  #8  
Junior Member
Thread Starter
 
Morello's Avatar
 
Join Date: Jul 2009
Location: Orange County, CA
Posts: 418
Total Cats: 45
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.
Morello is offline  
Old 06-22-2016, 06:50 PM
  #9  
Junior Member
Thread Starter
 
Morello's Avatar
 
Join Date: Jul 2009
Location: Orange County, CA
Posts: 418
Total Cats: 45
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.
Morello is offline  
Old 06-25-2016, 02:46 AM
  #10  
Senior Member
 
gooflophaze's Avatar
 
Join Date: May 2007
Location: Atlanta
Posts: 997
Total Cats: 156
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);
}
gooflophaze is offline  
Old 06-28-2016, 09:30 PM
  #11  
Junior Member
Thread Starter
 
Morello's Avatar
 
Join Date: Jul 2009
Location: Orange County, CA
Posts: 418
Total Cats: 45
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.
Morello is offline  
Old 06-29-2016, 12:18 PM
  #12  
Senior Member
 
gooflophaze's Avatar
 
Join Date: May 2007
Location: Atlanta
Posts: 997
Total Cats: 156
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.
gooflophaze is offline  
Old 06-29-2016, 12:32 PM
  #13  
Junior Member
Thread Starter
 
Morello's Avatar
 
Join Date: Jul 2009
Location: Orange County, CA
Posts: 418
Total Cats: 45
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 02:16 PM.
Morello is offline  


Thread Tools
Search this Thread

All times are GMT -4. The time now is 03:46 AM.