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!
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!
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);
}
}
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.
There was another thread of someone else working on a similar project but I can't find it at the moment.
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.
There was another thread of someone else working on a similar project but I can't find it at the moment.
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'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'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.
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.
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.
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.
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);
}
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);
}
I still need to figure out why the EGO request packets from my megasquirt are reporting 597 and 605 for myvaroffset.
MS's request code is kinda stupid (at least it was 2 years ago). It spams the bus. You can write directly to the block
ffset 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.
ffset 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.
MS's request code is kinda stupid (at least it was 2 years ago). It spams the bus. You can write directly to the block
ffset 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.
ffset 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.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; Jun 29, 2016 at 02:16 PM.





