Trouble with Serial between 2 Teensy's

Status
Not open for further replies.

tranger

Member
I'm working on a project that requires the microcontroller to communicate with an Engine Control Module over a Serial connection. I had a version of it working with an UNO, but needed the code space of a Teensy to add functionality. My first attempt to running the UNO code had intermittent problems. Data was flowing well and then it didn't. To help with debugging, I decided to use two Teensy's, using one to emulate the ECM. This way I could work and test all on the bench.

This has not worked out. I've worked on it, on and off, for quite a while and just can't seem to find a way to move forward. I realized that in order to test the project device, basically a display unit, with a ECM emulator, that the emulator (and it's software) has to work too. I tried to validate the emulator with my older UNO device, but it was inconsistent, working some of the time and bugging out at others.

I've hooked up a logic analyzer and it shows that the emulator data stream disappears then that line is connected between devices, but works fine otherwise. This is the problem I'd like to resolve.

At this time I've got a 3.2 emulating the ECM and a 3.5 acting as the display. (I have a couple of LC's, a 3.2 and a 3.6 that could be swapped out if needed).

Here's the emulator code:

PHP:
#define teensySerial Serial1 //for ECM comms uses pin 0 for rx and pin 1 for tx
//#define serialTimeOut 5   //timeout for serial communications

byte tps;

const int inPin = 10;
const int sendPin = 13;

byte teensyData[107];  //

//live data to be sent
byte ecmData[107] = {
0x01, 0x42, 0x00, 0x64, 0xff, 0x02, 0x06, 0x7b, 0x08, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x20, 0x58,
0x20, 0x76, 0x36, 0x76, 0x36, 0x2e, 0x00, 0x0d, 0x9b, 0x04,
0x56, 0x02, 0x7e, 0x02, 0x00, 0x00, 0x89, 0x02, 0x38, 0x06,
0xf2, 0x03, 0x00, 0x00, 0xe8, 0x03, 0xe8, 0x03, 0xe8, 0x03,
0xe8, 0x03, 0x86, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xfe, 0x32, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x97, 0x00, 0x2e, 0x01, 0xf1, 0xc2, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0x03, 0x34};


int ByteOffset = 0;
elapsedMillis sinceLastRead;  // for timeout

// ECM communication constants
const byte SOH = 0x01; // Start of Header
const byte PC =  0x00; // PC as target
const byte ECM = 0x42; // ECM as target
const byte SOT = 0x02; // Start of Text
const byte EOH = 0xff; // End of Header
const byte EOT = 0x03; // End of Text (included in length of text)
const byte ACK = 0x06; // Acknowledge code for 'successful' all else means problem

//Set request code to send data from ECM
  const byte ecmReadCmd[9]={
  SOH,
  PC, 	// Sender
  ECM, 	// Receiver
  0x02, // Length
  EOH,
  SOT,
  0x43, // OpCode for send live data
  EOT, 
  0xFD};	//checksum: xor checksum bytes 2 thu 8. 0xE8 for "get version" 0xFD for "get run data"  NOTE: the first byte is excluded from the checksum
  
//----------------------------------------------------------------------------------------------------
void setup(){
  
  
  teensySerial.begin(9600);
  delay(1000);
  teensySerial.setTimeout(20);
  //open debug port
  Serial.begin(115200);  // Serial Monitor on PC
  delay(1000);
  Serial.println("Emulation Start");
  
  pinMode(inPin, OUTPUT);
  digitalWrite(inPin, LOW);
  
  pinMode(sendPin, OUTPUT);
  digitalWrite(sendPin, LOW);
  
} 
//-------------------------------------------------------------------------------------------------

// Main Processing Loop
void loop(void)
 {
	
	//ByteOffset = 0;
	
	if (teensySerial.available())  // there is a byte in the incoming buffer
	{
	digitalWrite(inPin, HIGH);
	teensySerial.readBytes(teensyData,9);
	digitalWrite(inPin, LOW);
	
		if (teensyData[0] == ecmReadCmd[0] &&   //0x01
		teensyData[1] == ecmReadCmd[1] &&    	//0x00
		teensyData[2] == ecmReadCmd[2] &&    	//0x42   
		teensyData[3] == ecmReadCmd[3] &&    	//0x02  
		teensyData[4] == ecmReadCmd[4] &&    	//0xff
		teensyData[5] == ecmReadCmd[5] &&    	//0x02
		teensyData[6] == ecmReadCmd[6] &&    	//0x43
		teensyData[7] == ecmReadCmd[7] &&    	//0x03
		teensyData[8] == ecmReadCmd[8])      	//0xfd
		{ 
			// request ok, send payload
			Serial.println("header good");
			
			//tps = random (200);
			//secmData[25] = tps;
			
			digitalWrite(sendPin, HIGH);
			teensySerial.write(ecmData, 107);
			delay(20);
			digitalWrite(sendPin, LOW);
				
		}
		
	}
	
 }

And the project display:

PHP:
#include <Wire.h>  // Include Wire if you're using I2C
#include <SFE_MicroOLED.h>  // Include the SFE_MicroOLED library

#define ecmSerial Serial1 //for ECM comms uses pin 0 for rx and pin 1 for tx

//////////////////////////
// MicroOLED Definition //
//////////////////////////
//The library assumes a reset pin is necessary. The Qwiic OLED has RST hard-wired, so pick an arbitrarty IO pin that is not being used
#define PIN_RESET 9  
//The DC_JUMPER is the I2C Address Select jumper. Set to 1 if the jumper is open (Default), or set to 0 if it's closed.
#define DC_JUMPER 1 

//////////////////////////////////
// MicroOLED Object Declaration //
//////////////////////////////////
MicroOLED oled(PIN_RESET, DC_JUMPER);    // I2C declaration

const int readPin = 13;

byte ecmData[107]; //live data reported from ECM

// ECM communication constants
const byte SOH = 0x01; // Start of Header
const byte PC =  0x00; // PC as target
const byte ECM = 0x42; // ECM as target
const byte SOT = 0x02; // Start of Text
const byte EOH = 0xff; // End of Header
const byte EOT = 0x03; // End of Text (included in length of text)
const byte ACK = 0x06; // Acknowledge code for 'successful' all else mean problem

//Set request code to read data from ECM
  const byte ecmReadCmd[9]={
  SOH,
  PC, 	// Sender
  ECM, 	// Receiver
  0x02, // Length
  EOH,
  SOT,
  0x43, // OpCode for send live data
  EOT, 
  0xFD};	//checksum: xor checksum bytes 2 thu 8. 0xE8 for "get version" 0xFD for "get run data"  NOTE: the first byte is excluded from the checksum

//----------------------------------------------------------------------------------------------------
void setup(){
  
  ecmSerial.begin(9600);  // baud rate for the ECM is 9600
  delay(1000);
  ecmSerial.setTimeout(20);
  //open debug port
  Serial.begin(115200);  // Serial Monitor on PC
  delay(1000);
  Serial.println("Starting...");
  
  pinMode(readPin, OUTPUT);
  digitalWrite(readPin, LOW);
  
  oled.begin();    // Initialize the OLED
  oled.clear(ALL); // Clear the display's internal memory
  oled.display();  // Display what's in the buffer (splashscreen)
  delay(1000);     // Delay 1000 ms
  oled.clear(PAGE); // Clear the buffer.
  
  oled.setFontType(0);  // Set font to type 0 (5 x 8)
  oled.clear(ALL);     // Clear the page  
  oled.setCursor(0,0); // Set cursor to top-left
  oled.println("   Start");
  oled.println("Line 1");
  oled.println("LIne 2");
  oled.println("Line 3");
  oled.display();

}

//Send data request to ECM
void requestData(void) {
  ecmSerial.write(ecmReadCmd,9);
}
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
void readLiveData(void)
	{
	int numBytes = 0;
	byte chksum = 0x00;
	
	delay (20); // wait for buffer to fill
	
	if (ecmSerial.available())
		{
		//digitalWrite(readPin, HIGH);
		numBytes = ecmSerial.readBytes(ecmData,107);
		//digitalWrite(readPin, LOW);
		}
		if (numBytes == 107)
			{
			if (ecmData[0] == SOH &&
				ecmData[1] == ECM && 	
				ecmData[2] == PC  &&  				
				ecmData[3] == 100 &&     // total of 107 bytes - 7 which are not counted
				ecmData[4] == EOH &&
				ecmData[5] == SOT)
				{
				if (ecmData[6] == ACK)
					{
						for (int i=1; i<106; i++)  // skip the SOH and the chksum data to calculate the chksum
						{
						chksum = chksum ^ ecmData[i];
						}
					//Serial.println(chksum, HEX);
					if (chksum == ecmData[106]) // if checksum is ok, process data  
						{
						Serial.println("ok");
						oled.println("ok");
						oled.display();
						//processLiveData();
						}
						else
						{
						Serial.println("Chksum error");
						}
					}
					else
					{
					Serial.println("Ack error");
					}
				}
		        else
				{
				Serial.println("Header error");
				}
			}
			else
			{
			Serial.print("size = ");
			Serial.println(numBytes);
			Serial.println("Timeout / size error");
			}
	}
//--------------------------------------------------------------------------------------------------

// Main Processing Loop
void loop(void)
 {
			
	requestData();
	//delay(5);
	readLiveData();
		
    delay(500);
}  // end of loop

Wiring is Gnd to Gnd, ECM Tx to Disp Rx, ECM Rx to Disp Tx. There is also an OLED display on the 3.5. It uses the I2C port. Right now is isn't doing much, but I'll use it to help debug other stuff if I can ever get past the comms problem.

The logic analyzer shows the results when the ECM Tx is disconnected between to devices. When I connect that channel, the pin status is always low. It does not idle high.

Any suggestions on how to troubleshoot this thing?
 

Attachments

  • 2018-06-09_15-13-26.png
    2018-06-09_15-13-26.png
    52.7 KB · Views: 63
the project display:
Code:
// Main Processing Loop 
void loop(void) 
 { 
             
    requestData(); 
    //delay(5); 
    readLiveData(); 
         
    delay(500); 
}  // end of loop
delay() function isn’t a good way to make your program wait, if you have to respond to serial data stream with long delay you run the risk of a serial buffer overflow and as a result data loss.
Serial1 RX buffer is only 64 bytes (default).
 

Actually I did that, since the outgoing payload on the emulator is 107 bytes.

#ifndef SERIAL1_TX_BUFFER_SIZE
#define SERIAL1_TX_BUFFER_SIZE 128 // number of outgoing bytes to buffer
#endif
#ifndef SERIAL1_RX_BUFFER_SIZE
#define SERIAL1_RX_BUFFER_SIZE 128 // number of incoming bytes to buffer
#endif

There another thing I tried that I should mention. On the emulator I switched to Serial2 and then Serial3. Same results.

The weird thing is that the Tx line of the emulator stays at a logic zero at all times, while that pin is connected to the Rx line of the display unit.

I'll post that logic data soon.

Thanks!
 
Wiring and logic trace attached.

The logic label sense is with respect to the ECM emulator (3.2). It Rx's a request of 9 bytes and then Tx's a payload of 107 bytes.

So the ECM emulator Tx is ALWAYS low. Like it is being pulled down?

If I pull the Tx wire to break the connection with the Display unit (3.5), the Tx clocks out 107 bytes every time.:confused:
 

Attachments

  • LogicTrace 3pt2 ECM 3pt5 Disp.png
    LogicTrace 3pt2 ECM 3pt5 Disp.png
    82.3 KB · Views: 71
  • 20180609_202937.jpg
    20180609_202937.jpg
    81.5 KB · Views: 99
  • 2018-06-09_20-48-02.jpg
    2018-06-09_20-48-02.jpg
    91.2 KB · Views: 85
Hmmmm, i may be reading the SFE_MicroOLED library wrong, but it looks like dcPin may not be set and defaults to 0, which would interfere with your pin 0 usage (RX1)
 
Hmmmm, i may be reading the SFE_MicroOLED library wrong, but it looks like dcPin may not be set and defaults to 0, which would interfere with your pin 0 usage (RX1)

That actually sounds very promising... I had stripped out all OLED stuff from the Display Teensy a few minutes ago. Having some issues. Will try to sort it out.
 
That's it! Not sure how to fix it, but removing all ref's to the OLED brought everything back to normal.

Thanks Manitou and Chris. Now I can move forward to the next problem. ;-)
 
That's it! Not sure how to fix it

Assuming you are using https://github.com/esp8266/Basic/tree/master/libraries/Micro_OLED_Breakout
here is a possible fix for begin() in SFE_MicroOLED.cpp: add interface check, resulting in
Code:
if (interface != MODE_I2C) {
	dcport	= portOutputRegister(digitalPinToPort(dcPin));
	dcpinmask	= digitalPinToBitMask(dcPin);
	dcreg	= portModeRegister(digitalPinToPort(dcPin));

       pinMode(dcPin, OUTPUT);
}

untested.

EDIT: Same fix for https://github.com/sparkfun/SparkFun_Micro_OLED_Arduino_Library

lib probably works as-is for SPI mode, and SPI mode is faster, but you need more pins.

EDIT 2: I filed an issue and sparkfun has fixed their version on github 6/11/18
 
Last edited:
Status
Not open for further replies.
Back
Top