USB Serial Differences: ArduinoMega256 and Teensy 3.2

raysot

New member
I have an application that sends data using Serial.print().

Using the same code base, the Arduino sends/receives data fine over USB but the Teensy 3.2 doesn't, due to some weird USB/HID/Serial differences, which doesn't really make it a true serial port.

Is there any way to get the Serial data on the USB to behave like it behaves on the 256? Is there a library that might be available? I ask because if not, I'm looking at retooling about 20 circuit cards.
 
If you want (true) serial output use Serial1, Serial2 or Serial3.
Serial (USB) outputs to USB or Hid.
 
If you want (true) serial output use Serial1, Serial2 or Serial3.
Serial (USB) outputs to USB or Hid.

I wish I could use the dedicated serial ports on the Teensy, but in my particular application, all serial comms need to go through the USB port.
 
Sorry, I have no idea what the issue is. I.e what you are trying to do?

The Serial object sends and receives data using USB.

Serrial1 uses io pins 0 and 1. Noe on some of these AVR arduino boards, the processor does not have built in support for USB, so they have additional hardware that does USB and then talks to AVR board through two io p8ns connected to a hardware hart. Typically pins 0 and 1.

So what exactly are you looking for the teensy to do?
 
Is there any way to get the Serial data on the USB to behave like it behaves on the 256?

To first answer directly, yes, there is probably a way.

Teensy's USB serial already works pretty similarly to Arduino Mega's USB serial. They both use CDC protocol, if you're using a genuine Arduino Mega with the 16U2 chip for the USB interface. There are some differences, like how USB auto reset is handled (DTR or RTS vs special baud rate) and whether the USB disconnects and reappears on each upload, not to mention the incredible difference in speed.

But maybe you are using a clone or counterfeit of Arduino Mega which has a different USB serial chip? Many 3rd party "Mega" products exist on the market, most using chips like FTDI and CH340, but some do have the same hardware as Arduino's original. They're all USB serial, but those other chips have different protocols from CDC, which achieve the same thing but with very minor differences in how they actually do it. When we're talking about whatever minor difference in communication which is all USB serial, this sort of detail matters. So far all we know is you're using "ArduinoMega256", which doesn't quite match any Arduino product. The closest I could find on Arduino's website is Arduino Mega 2560 Rev3. Maybe you meant 2560 and just accidentally dropped the '0'?

Details matter when trying to figure out why one type of USB serial is working and another is not. The other really important details involve what you're actually doing on the PC side.

Maybe you're running software which keeps the port open? Or perhaps you're only using Arduino Serial Monitor? Perhaps you have Arduino 2.0.3? Or maybe you have Arduino 1.8.19 and Teensyduino 1.56? Or maybe you're using one of those Arduino versions and also some other non-Arduino software which accesses the serial port? And inside Arduino, in the Tools > Port menu, perhaps you selected something under "Serial ports" or maybe instead "Teensy ports" (which you clicked causes Arduino IDE to access the port differently) Maybe you have Microsoft Windows and we talk of this serial port as a COM port? Or maybe you use MacOS and it's a /dev/cu.usbmodemxxx device or a /dev/ttyACMx on Linux? Which version could matter, especially if you have any pre-10 version of Windows (those old versions had buggy USB drivers). Perhaps your program is keeping the port open while Teensy / Mega reboots? (on Mega the USB chip stays running while the CPU chip reboots but on Teensy it's only 1 chip so CPU & USB reboot together) Or maybe your non-Arduino program opens the port and expects Mega / Teensy to reboot automatically? Maybe you don't know exactly what subtle behavior whatever (unknown to us) software you use depends upon... all you know is it works with Mega but not with Teensy... but we don't know *anything* about what software you're actually using, how you use it, what happens with Teensy / Mega as part of that usage, or even which operating system it's all running upon!

What sort of thing your code on the Arduino Mega side does could also play an important factor. We're completely in the dark about that. Usually we ask to see the code you're running (or a small example which is confirmed to reproduce the problem if your program is large). Maybe you noticed "Forum Rule" in red at the top of every page on this forum? It's not just an arbitrary rule... showing us the code lets us help you much better and saves everyone a lot of time (spent actually solving your problem rather than explaining you need to give more info).

We want to help you get this problem resolved. I hope you can understand how resolving the difference between communication between 2 different boards which are both USB serial involves really digging into the subtle differences, but so far you've told us almost no detailed info, not even enough for much guesswork.
 
Thanks Paul, I'll try to add a bit more color here...

Here's a code snippet as to what the controller side is doing:

The Setup() Code:

Code:
Serial.begin(115200);
  while(!Serial);
The Reader code:

Code:
void getDataFromFS2020_V1() {

  String temp = "";

  if (Serial.available() > 0) {
    Serial.readStringUntil('@'); //Look out for first Indicator
    id = Serial.readStringUntil('/').toInt();     //Reads ID
    IDindex = Serial.readStringUntil('=').toInt();  //Reads index
    stringValue = Serial.readStringUntil('$');    //Reads value as String
    intValue = stringValue.toInt();               //Converts value to integer
    floatValue = stringValue.toFloat();           //Converts value to Float
  }

  switch (id) {
    //------------------------------
    //---- OIL PRESSURE #1      ----
    //------------------------------
    case 218: //to add a new variable just add another case with your id
      if (IDindex == 1)
      {
         oilPressure_1_Raw = floatValue;
      }
      break;

    //------------------------------
    //---- OIL TEMP #1      ----
    //------------------------------
    case 220: //to add a new variable just add another case with your id
      if (IDindex == 1)
      {
        oilTemp_1_Raw = floatValue;
      }
      break;

    default:
      break;
  }
}


The Writer code:

Code:
[INDENT]void sendCommandToFS2020(int id, int value) {
  String output =  "@" + String(id) + "/" + String(value) + "$";
  Serial.print(output);
}

[/INDENT]

I have several Mega256 cards, most are genuine with a few knockoffs intermixed. All the Mega256 cards use this code without issue.

I also have several Teensy 3.2 cards, all of them purchased directly from PJRC. All of them fail.

I don't have visibility into the source code for the middleware app. However, the app does see every Teensy 3.2 and registers it on the correct COM port. They just don't send/receive.

I am using the Arduino IDE v1.8.19, and TeensyDuino v1.57

I have read that the Teensy line of cards will ignore attempts to set the BAUD rate, and instead negotiates the fastest rate possible. I'm not sure if that's the case here. I suspect it might be since the card is registered on a COM port but can't otherwise communicate.

Hope that's enough info...
 
It may but probably not enough information.

In most cases like this, I would hope to see some simple sketch that shows the issue. For example, with the code you posted, none of the variables are defined and the like,
so no way to compile it and try it out.

I did try a sub-set of the code with defining some variables, just to see if the parsing code works.

Code:
void setup() {
  Serial.begin(115200);
  while (!Serial)
    ;
}
void getDataFromFS2020_V1() {

  String temp = "";

  if (Serial.available() > 0) {
    Serial.readStringUntil('@');                        //Look out for first Indicator
    int id = Serial.readStringUntil('/').toInt();       //Reads ID
    int IDindex = Serial.readStringUntil('=').toInt();  //Reads index
    String stringValue = Serial.readStringUntil('$');   //Reads value as String
    int intValue = stringValue.toInt();                 //Converts value to integer
    float floatValue = stringValue.toFloat();           //Converts value to Float

    Serial.print("ID:");
    Serial.print(id, DEC);
    Serial.print(" Index:");
    Serial.print(IDindex, DEC);
    Serial.print(" String:");
    Serial.print(stringValue);
    Serial.print(" intVal:");
    Serial.print(intValue, DEC);
    Serial.print(" float Val:");
    Serial.print(floatValue, 2);
    Serial.println();
  }
}

void loop(){
  getDataFromFS2020_V1();
}
You did not provide any sample data that you would expect to receive and what you would expect and what results you saw.

For example, I typed in: @20/30=40$
And the output to serial terminal: ID:20 Index:30 String:40 intVal:40 float Val:40.00

Likewise: @100/2=32.75$
ID:100 Index:2 String:32.75 intVal:32 float Val:32.75

Note: I did this on T4... Could go into my cabinets and find a T3.2, but again not sure what I would be testing for.

From your description, so far all we know is something is not working.

Also know very little about the setup. The T3.2 is plugged into something, and it knows it is there. But not what it is, nor what it is doing or not doing correctly.

What else is the Teensy doing? Are some/all of the IO pins being used to do other stuff?
If it were me, I would probably instrument the code, to see if one can figure out what it is doing or not doing.

Example maybe use Serial1 to output debug messages. Like maybe print out those values like I did and see if that part is working properly. Would hook up a USBToSerial device connected up to pins 0, 1 and connected up to a PC, with terminal program to receive the data. Note: the USB to Serial device could easily be another teensy.

Thing I try (again more less throwing data)

a) Instrument the code like I mentioned, probably incluing also printing out timing information (like millis()... And mabye try same on Mega boards, to get an idea of what is being received and when. Would also instrument the output again to see what iis being sent back and when.

With that compare the outputs of the two boards to see if anything jumps out.

b) In most cases the Teensy runs a whole lot faster than Mega board. So maybe add in some delays between when you receive something and when you reply. Speed wise we are talking both processor speed differences, but also on the Mega after the board receives the USB packet, it then has to send the data to the Atmega2560 board at 115200, So there is for sure a delta time between when the USB data was sent from host, until the processor sees the data, and likewise when you write data back on Serial, the AVR board has to send the data to the communication chip, which then sends a packet back to the host.
So you may need to add in some delays

c) The USB Start up time is slower in the Teensy then the AVR board. In the case of using a Teensy. Serial.begin(115200) the 115200 more or less does nothing, but the begin has built in delays. The Teensy Serial.begin() is optional and added in extra delays to allow the host more time to initialize.

d)...

Hopefully if you do a) you will have some useful information, that can help point toward what the diferences might be.
 
Agree with Kurt, since you can't see into what this middleware app is doing, the path forward involves adding stuff into the code on Teensy so you can see which part is (and isn't) running.

The simple but primitive way would use LEDs+resistors on unused pins. Just use pinMode OUTPUT on all of them as the first thing you do in setup(), and then sprinkle digitalWrite() throughout the code to get some idea which parts are and aren't running.

For example, in the startup code:

pinMode(13, OUTPUT);
Serial.begin(115200);
digitalWrite(13, HIGH);
while(!Serial);
digitalWrite(13, LOW);

This can tell you whether it's getting stuck at that "while(!Serial);", which is a possibility if the middleware app runs on Windows and opens the port in an unusual way that doesn't let Teensy know the PC side has the port open. (my understanding is Linux and MacOS don't let programs do that, but Windows can)

Likewise for the transmitting and receiving code, add LEDs and digitalWrite(). Also do this on Arduino Mega, so you can see what the normal behavior is like.

The 2 downsides to simple LED troubleshooting is you get relatively little info, and human vision is pretty limited for seeing many types of rapid behavior. On the speed side, you might experiment with adding delay() in places, probably first on Arduino Mega, and experiment with how the mystery middleware app tolerates delays. If added delays give you more visually distinctive blinking with the middleware still working, you can leverage that to "see" what its doing and compare with how the LEDs blink when the same code runs on Teensy.

LEDs have a nice simplicity, that you don't depend on any other software or complex setup, but you're really limited in how much info your eyes can see. The more complex way would be to add another serial communication channel, so you can add lines like Serial3.println("code was running here"); or SerialUSB1.println(someVariable); This way you can clearly see in Arduino Serial Monitor or Coolterm what really happened and you can print the actual variables involved. This is so much more powerful than just blinking LEDs... but it does require setup of another serial port.

The simplest way might be to use one of Teensy's non-USB hardware ports like Serial3 and a dedicated USB-serial cable to receive its transmit pin. If you don't have that hardware, another Teensy will do. Just program it with File > Examples > Teensy > USB_Serial > USBtoSerial. Of course with 2 Teensy boards connected to your PC, be careful in Arduino that you're uploading to the intended board.

Adding more hardware and software can make any already difficult situation even more frustrating if it doesn't work reliably. While it's extra work, I would highly recommend thoroughly testing whatever extra serial hardware you add with known-good and simple programs on Teensy and communication with Arduino Serial Monitor before putting it to use troubleshooting complicated code talking to the closed source middleware.

The other way which doesn't need any extra wires, but might add more unknowns about COM ports, is to use Tools > USB Type to configure Teensy to implement 2 serial ports. Then it will appear as 2 COM ports. Use SerialUSB1 to print to the other port. Whether this is actually easier than adding wires and more hardware depends on whether you can clearly know which COM ports (or devices in using MacOS or Linux) are which.

Whatever approach you use, the idea is to gain insight into which code really is running on the Teensy side when the middleware app communicates.

Arduino Mega also offers multiple non-USB serial ports, but it can't do anything like Tools > USB Type. You can do the same on Mega's extra serial ports to gain insight into what the closed source middleware app is really doing, and give yourself the "known good" output for comparison to see what's happening differently when the middleware communicates with Teensy.

While none of this really answers the question of what's really going wrong, hopefully you can see a path forward to finding that answer.
 
I posted the entire sketch below. The code loads fine for both the am256 and t3.2. On the am256 it works. Execution on the t3.2 fails.

In fact, it never gets beyond
Code:
while (!Serial)
...and never gets to the "Happy Clap" section and beyond.

In the IDE I have it set to USB Type = Serial.

This happens across all my Teensy modules. (I have 5 so far. Ordered a few 4.0's yesterday. Probably the same issue with those as well.)

So... am I in the wrong forum? My OP was intentionally lacking in detail because I didn't want this to be a discussion that dove right down into the weeds. There are some architectural differences between the USB on Arduino and the USB on Teensy.

One point that caught my eye is adding delay(s) in the loop() section as I have been bitten by that before. Slowing the loop() down on a 3.2 is definitely beneficial and I have seen it work. No joy at all on this issue, even with delays as high as 100ms.

Note: Using any other serial port is not an option. Serial comms MUST go through the USB port.

Code:
#include <LiquidCrystal_I2C.h>
#include <JC_Button.h>  // REF: [url]https://github.com/JChristensen/JC_Button[/url]

// Button BTN_AP_MASTER(53);       // Arduino Button   
   Button BTN_AP_MASTER(16);       // Teensy Button   

int     id = -1;        //ID    memory
int     IDindex;        //Index memory
String  stringValue; //Value memory String  format
int     intValue;       //Value memory integer format
float   floatValue;   //Value memory float   format

struct DataSet {
  int id;             //ID    memory
  int IDindex;          //Index memory
  String stringValue; //Value memory String  format
  int intValue;       //Value memory integer format
  float floatValue;   //Value memory float   format
};

float oilPressure_1_Raw;
float oilTemp_1_Raw;

byte FLAG_AP_MASTER = 0; // I know... Flags are bad...

LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup() {
  BTN_AP_MASTER.begin();  

  // --- OUTPUTS --- //
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(29, OUTPUT);         // AP Button Light Enable
  pinMode(13, OUTPUT); 
  
  // --- INPUTS --- //
  //pinMode(53, INPUT_PULLUP);
  pinMode(15, INPUT_PULLUP);
  pinMode(16, INPUT_PULLUP);
  pinMode(17, INPUT_PULLUP);

  // --- Slow Clap --- //  
  digitalWrite(13, 0); delay(1500); 
  digitalWrite(13, 1); delay(1500);
  digitalWrite(13, 0); delay(1500); 
  digitalWrite(13, 1); delay(1500);
  digitalWrite(13, 0); delay(1500); 
 
  Serial.begin(115200);
  while(!Serial);
 
  // --- Happy  Clap --- //
  digitalWrite(13, 0); delay(500); 
  digitalWrite(13, 1); delay(500);
  digitalWrite(13, 0); delay(500); 
  digitalWrite(13, 1); delay(500);
  digitalWrite(13, 0); delay(500); 
  digitalWrite(13, 1); delay(500);
  digitalWrite(13, 0); delay(500); 
  digitalWrite(13, 1); delay(500);
  digitalWrite(13, 0); delay(500); 
  digitalWrite(13, 1); delay(500);
  digitalWrite(13, 0); delay(500); 
  digitalWrite(13, 1); delay(500);
  digitalWrite(13, 0); delay(500); 
  digitalWrite(13, 1); delay(500);
  digitalWrite(13, 0); delay(500); 
  digitalWrite(13, 1); delay(500);

  lcd.init();                      // initialize the lcd
  lcd.backlight();
 

  // Guage Test
  analogWrite(2, 255);
  analogWrite(3, 255);
  delay(2000);
  analogWrite(2, 0);
  analogWrite(3, 0);

  digitalWrite(29, LOW); // Enable on BTN lights
  digitalWrite(0, LOW);  // Turn on BTN lights


}

void loop() {
 
  delay(20);

  BTN_AP_MASTER.read();

  if (BTN_AP_MASTER.wasPressed())
  {
    if (FLAG_AP_MASTER == 1) 
    {
      FLAG_AP_MASTER = 0;
      Serial.print("@88/0$");
    }
    else                    
    {
      FLAG_AP_MASTER = 1;
      Serial.print("@88/1$");
    }
  }

  getDataFromFS2020_V1(); //Handling of the data inside the function

  int oilPressure_1 = fMap(oilPressure_1_Raw, 0, 200, 0, 255);
  analogWrite(2, oilPressure_1);

  if(oilPressure_1 > 50.0){digitalWrite(13,1);}
  else{digitalWrite(13,0);}


  if (oilTemp_1_Raw < 0) {oilTemp_1_Raw = 0;} // Force negative oil temps to 0. Gauge can't do (-) values.
  int oilTemp_1 = fMap(oilTemp_1_Raw, 0, 170, 0, 255);
  analogWrite(3, oilTemp_1);

}

//--------------------------------------------------------//
//--------------- Button/Switch Methods -------------------//
//--------------------------------------------------------//

//--------------------------------------------------------//
//------------------ Helper Methods ----------------------//
//--------------------------------------------------------//
float fMap(float x, float in_min, float in_max, float out_min, float out_max)
{
  return floor((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min);
}

void getDataFromFS2020_V1() {

  String temp = "";

  if (Serial.available() > 0) {
    Serial.readStringUntil('@'); //Look out for first Indicator
    id = Serial.readStringUntil('/').toInt();     //Reads ID
    IDindex = Serial.readStringUntil('=').toInt();  //Reads index
    stringValue = Serial.readStringUntil('$');    //Reads value as String
    intValue = stringValue.toInt();               //Converts value to integer
    floatValue = stringValue.toFloat();           //Converts value to Float
  }

  switch (id) {
    //------------------------------
    //---- OIL PRESSURE #1      ----
    //------------------------------
    case 218: //to add a new variable just add another case with your id
      if (IDindex == 1)
      {
        lcd.setCursor(0, 0); lcd.println("ID:    "); lcd.print(id);
        lcd.setCursor(0, 1); lcd.println("Index: "); lcd.print(IDindex);
        lcd.setCursor(0, 2); lcd.println("Value: "); lcd.print(stringValue);

        oilPressure_1_Raw = floatValue;
      }
      break;

    //------------------------------
    //---- OIL TEMP #1      ----
    //------------------------------
    case 220: //to add a new variable just add another case with your id
      if (IDindex == 1)
      {
        oilTemp_1_Raw = floatValue;
      }
      break;

      default:
      break;
  }
}


DataSet getDataFromFS2020_V2() {
  DataSet returnDataSet;
  String temp = "";

  if (Serial.available() > 0) {
    Serial.readStringUntil('@'); //Look out for first Indicator
    returnDataSet.id = Serial.readStringUntil('/').toInt();         //Reads ID
    returnDataSet.IDindex = Serial.readStringUntil('=').toInt();      //Reads index
    returnDataSet.stringValue = Serial.readStringUntil('$');        //Reads value as String
    returnDataSet.intValue = returnDataSet.stringValue.toInt();     //Converts value to integer
    returnDataSet.floatValue = returnDataSet.stringValue.toFloat(); //Converts value to Float
  }
  return returnDataSet;
}

void sendCommandToFS2020(int id, int value) {
  String output =  "@" + String(id) + "/" + String(value) + "$";
  Serial.print(output);
}

void sendCommandToFS2020(int id, double value) {
  String output =  "@" + String(id) + "/" + String(value) + "$";
  Serial.print(output);
}

void sendCommandToFS2020(int id, float value) {
  String output =  "@" + String(id) + "/" + String(value) + "$";
  Serial.print(output);
}

void sendCommandToFS2020(int id, String value) {
  String output =  "@" + String(id) + "/" + String(value) + "$";
  Serial.print(output);
}

void sendCommandToFS2020(String id, String value) {
  String output =  "@" + id + "/" + value + "$";
  Serial.print(output);
}
 
Last edited:
What is the Host device and 'Writer code' software in use? What OS?

The Teensy units as noted do well as USB Serial devices with IDE's Serial Monitor or other. Got it working with an Android app too but as noted it took setting a control line (DTR or RTS) to complete the connection and allow transfer. That was some time ago and not tried for the past new phone or two so not installed now.

Without the Host code in the same setup and the LCD and button setup and code it would take modifying the code to work against Serial Monitor to emulate the USB input. In doing that there is no doubt it would work on this Windows machine with either the IDE Serial Monitor program or the TyCommander alternative typically used.
 
In fact, it never gets beyond
Code:
while (!Serial)
...and never gets to the "Happy Clap" section and beyond.

Have you tried simply deleting "while (!Serial)" ?

Strong possibility the middleware app is using a Windows function to open the port without raising DTR, which indicates to Teensy that the port has been opened.

Just delete that line and upload again to Teensy. Does it then work? Or at least fail in some other way than getting stuck waiting?
 
If it is NOT getting past
Code:
while (!Serial)
it means that the Teensy is NOT seeing the computer USB Serial port.
It would be better written as:-
Code:
Serial.begin;
While (!Serial && millis() < 5000){};
This will case the Teensy to wait for up to 5 seconds for the USB Serial port to appear.
EDIT:Cross post with Paul
 
Just to explain a bit further, on Arduino Mega testing "Serial" as a boolean always returns true, because Mega has no way to detect DTR or anything else about the status of the USB connection.

So it's quite possible your (Windows base?) middleware app has never been properly signaling it has opened the port and the code you've been running on Arduino Mega all this time could have been technically wrong for what that middleware app really does, but the problem has never manifested because on Arduino Mega "while (!Serial)" does absolutely nothing.

So to make Teensy do the same as Arduino Mega, deleting that line that does absolutely nothing on Mega would indeed be the proper thing for running the code on Teensy.
 
Back
Top