Building my own digital WBO2-CAN Adapter - help? - Miata Turbo Forum - Boost cars, acquire cats.

Welcome to Miataturbo.net   Members
 


MEGAsquirt A place to collectively sort out this megasquirt gizmo

Reply
 
 
 
LinkBack Thread Tools
Old 06-17-2016, 01:32 PM   #1
Junior Member
Thread Starter
 
Join Date: Jul 2009
Location: Greenville SC
Posts: 261
Total Cats: 15
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!
Uncle Humjaba is offline   Reply With Quote
Old 06-17-2016, 01:35 PM   #2
Junior Member
Thread Starter
 
Join Date: Jul 2009
Location: Greenville SC
Posts: 261
Total Cats: 15
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);
}
}
Uncle Humjaba is offline   Reply With Quote
Old 06-17-2016, 07:56 PM   #3
Senior Member
iTrader: (8)
 
Join Date: Jan 2012
Location: Azusa, CA
Posts: 1,376
Total Cats: 80
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   Reply With Quote
Old 06-17-2016, 08:04 PM   #4
Junior Member
Thread Starter
 
Join Date: Jul 2009
Location: Greenville SC
Posts: 261
Total Cats: 15
Default

Quote:
Originally Posted by cyotani View Post
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)
Uncle Humjaba is offline   Reply With Quote
Old 06-17-2016, 08:23 PM   #5
Elite Member
 
codrus's Avatar
 
Join Date: Mar 2007
Location: Santa Clara, CA
Posts: 3,878
Total Cats: 344
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   Reply With Quote
Old 06-17-2016, 08:36 PM   #6
Junior Member
Thread Starter
 
Join Date: Jul 2009
Location: Greenville SC
Posts: 261
Total Cats: 15
Default

Quote:
Originally Posted by codrus View Post
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.
Uncle Humjaba is offline   Reply With Quote
Old 06-21-2016, 10:40 PM   #7
Boosting since 1984
iTrader: (1)
 
stefanst's Avatar
 
Join Date: Sep 2011
Location: Levittown
Posts: 1,198
Total Cats: 68
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   Reply With Quote
Old 06-22-2016, 11:14 AM   #8
Junior Member
Thread Starter
 
Join Date: Jul 2009
Location: Greenville SC
Posts: 261
Total Cats: 15
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.
Uncle Humjaba is offline   Reply With Quote
Old 06-22-2016, 07:50 PM   #9
Junior Member
Thread Starter
 
Join Date: Jul 2009
Location: Greenville SC
Posts: 261
Total Cats: 15
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.
Uncle Humjaba is offline   Reply With Quote
Old 06-25-2016, 03:46 AM   #10
Junior Member
 
Join Date: May 2007
Location: Atlanta
Posts: 384
Total Cats: 39
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 online now   Reply With Quote
Old 06-28-2016, 10:30 PM   #11
Junior Member
Thread Starter
 
Join Date: Jul 2009
Location: Greenville SC
Posts: 261
Total Cats: 15
Default

Quote:
Originally Posted by gooflophaze View Post
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.
Uncle Humjaba is offline   Reply With Quote
Old 06-29-2016, 01:18 PM   #12
Junior Member
 
Join Date: May 2007
Location: Atlanta
Posts: 384
Total Cats: 39
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 online now   Reply With Quote
Old 06-29-2016, 01:32 PM   #13
Junior Member
Thread Starter
 
Join Date: Jul 2009
Location: Greenville SC
Posts: 261
Total Cats: 15
Default

Quote:
Originally Posted by gooflophaze View Post
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 Uncle Humjaba; 06-29-2016 at 03:16 PM.
Uncle Humjaba is offline   Reply With Quote
 
 
Reply


Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are Off
Refbacks are Off



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