Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 18 of 18

Thread: Teensy 4.1 freezes after receiving serial command

  1. #1
    Junior Member
    Join Date
    Jan 2021
    Posts
    3

    Teensy 4.1 freezes after receiving serial command

    I am replacing an Arduino Uno with a Teensy 4.1, but I'm having trouble with serial communication. On the Uno, when I send "CHAN" from the serial monitor, the Arduino responds with "#OK." Using the same sketch on the Teensy, the Teensy seems to freeze after receiving "CHAN" and becomes unresponsive. To upload new code to the Teensy after it freezes, I have to press the physical program button.

    The end goal is to have the Teensy read thermocouple data and send that data to coffee roasting software on my Windows 10 machine. The coffee roasting software uses the "CHAN" message to initialize before sending a "READ" command to request temperature data.

    I did some searching on this forum and tried adding "delay(10)" to the loop without success. I have the Teensy 4.1 selected under "Teensy Ports."

    I have tried the "HelloSerialMonitor" Teensy example code and it works.

    Source code:
    Code:
    //Arduino --> Artisan Roaster Scope Communication Test
    // TC4 Communication Code adapted from FilePhil https://github.com/FilePhil/TC4-Emulator/blob/master/TC4-Emulator.ino
    
    
    bool unit_F = false; //true = °F - false = °C
    
    const long BAUD = 115200;
    
    double temp1 = 101;
    double temp2 = 102;
    double temp3 = 103;
    double temp4 = 104;
    String msg;
    
    // These variables are used for serial data input and parsing. Taken from example 4 and 5 of Robin2's thread "Serial Input Basics - updated" https://forum.arduino.cc/index.php?topic=396450.0
    const byte numChars = 32;
    char receivedChars[numChars];
    char tempChars[numChars];        // temporary array for use when parsing
    
    // variables to hold the parsed data
    char messageFromPC[numChars] = {0};
    
    int integerFromPC = 0;
    float floatFromPC = 0.0;
    
    
    boolean newData = false;
    // end serial variables
    
    
    void recvWithEndMarker() { //changed example 5 to eliminate start markers - Artisan uses newlines only, no start markers.
      //removed "recvInProgress" because it's not used in recvwithEndMarker - will this cause issues elsewhere?
      static byte ndx = 0;
      char endMarker = '\n';
      char rc;
    
      while (Serial.available() > 0) {
        rc = Serial.read();
    
        if (rc != endMarker) {
          receivedChars[ndx] = rc;
          ndx++;
          if (ndx >= numChars) {
            ndx = numChars - 1;
          }
        }
        else {
          receivedChars[ndx] = '\0'; // terminate the string
          ndx = 0;
          newData = true;
        }
      }
    }
    
    //============
    
    void parseData() {      // split the data into its parts
      // Artisan lines to parse: "CHAN;ijkl" "UNITS;u" "READ"
    
      char * strtokIndx; // this is used by strtok() as an index
    
      strtokIndx = strtok(tempChars, ";");     // get the first part - the string
      strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
      //Serial.println(messageFromPC);
    
      if (strcmp(messageFromPC, "READ") == 0) {
        Command_READ();
      }
      else if (strcmp(messageFromPC, "CHAN") == 0) {
        Serial.println("#OK");
      }
      else if (strcmp(messageFromPC, "UNITS") == 0) {
        strtokIndx = strtok(NULL, ";");
        //Serial.println(strtokIndx);
    
        if (strcmp(strtokIndx, "F") == 0) {
          unit_F = true;
          Serial.println("#OK Fahrenheit");
        }
        else if (strcmp(strtokIndx, "C") == 0) {
          unit_F = false;
          Serial.println("#OK Celsius");
        }
      }
      strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
      integerFromPC = atoi(strtokIndx);     // convert this part to an integer
    
      strtokIndx = strtok(NULL, ",");
      floatFromPC = atof(strtokIndx);     // convert this part to a float
    
    }
    
    //Send Data
    void Command_READ() {
      Serial.print("0.00,");
      Serial.print(temp1);
      Serial.print(",");
      Serial.print(temp2);
      Serial.print(",");
      Serial.print(temp3);
      Serial.print(",");
      Serial.println(temp4);
    }
    
    //Parsing Serial Commands
    void handleSerialCommand() {
    
      recvWithEndMarker();
      if (newData == true) {
        strcpy(tempChars, receivedChars);
        // this temporary copy is necessary to protect the original data
        //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        //showParsedData();
        newData = false;
      }
    }
    
    void setup() {
      Serial.begin(BAUD);
    }
    
    
    void loop() {
      handleSerialCommand();
      delay(10);
    }
    Error messages: When trying to upload after using the serial monitor (and freezing the Teensy) I get this message:
    "Unable to open COM4 for reboot request
    Windows Error Info: Access is denied.
    more ideas... https://forum.pjrc.com/threads/40632...l=1#post126667
    Teensy did not respond to a USB-based request to enter program mode.
    Please press the PROGRAM MODE BUTTON on your Teensy to upload your sketch."

    Hardware: Teensy 4.1, Windows 10 PC

    Wiring: Teensy 4.1 with Micro USB cable plugged into PC, no other wires connected.

    Software: Arduino IDE 1.8.13, Teensyduino 1.53

  2. #2
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    Pretty amazing that this works on a UNO:
    Code:
      strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
      integerFromPC = atoi(strtokIndx);     // convert this part to an integer
    
      strtokIndx = strtok(NULL, ",");
      floatFromPC = atof(strtokIndx);     // convert this part to a float
    Doodads:
    It will crash on the UNO, too.
    You just don't see it.
    The uno is prints the texts over a serial connection. The Teensy uses USB, async.
    If you add a delay(1); just before the marked lines above, the Teensy acts like a UNO, prints the texts, but will still crash (after that).

    The crash happens because of the NULL pointers. So this needs a rework.
    Last edited by Frank B; 01-10-2021 at 08:04 PM.

  3. #3
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    @Defragster, @Paul:

    Hehe, with #include <T4_PowerButton.h> it shows a Nullpointer-Exception.
    Without the include, it dies silent.

  4. #4
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    8,448
    Quote Originally Posted by Frank B View Post
    @Defragster, @Paul:

    Hehe, with #include <T4_PowerButton.h> it shows a Nullpointer-Exception.
    Without the include, it dies silent.
    Sounds like it or pieces of it would be a good addition to the core!

  5. #5
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    Yes, parts of it, not the whole lib.
    Maybe this, with the "power button" functionality added.

    ...and perhaps it should show a text that says more clearly that it was a nullptr?
    Code:
    Hardfault.
    Return Address: 0x000045A0 IPSR:0x03 CSFR: 0x00000082 XPSR: 0x81000000
        (DACCVIOL) Data Access Violation
        (MMARVALID) Accessed Address: 0x00000000

  6. #6
    Junior Member
    Join Date
    Jan 2021
    Posts
    3
    Quote Originally Posted by Frank B View Post
    Pretty amazing that this works on a UNO:
    Code:
      strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
      integerFromPC = atoi(strtokIndx);     // convert this part to an integer
    
      strtokIndx = strtok(NULL, ",");
      floatFromPC = atof(strtokIndx);     // convert this part to a float
    Doodads:
    It will crash on the UNO, too.
    You just don't see it.
    The uno is prints the texts over a serial connection. The Teensy uses USB, async.
    If you add a delay(1); just before the marked lines above, the Teensy acts like a UNO, prints the texts, but will still crash (after that).

    The crash happens because of the NULL pointers. So this needs a rework.
    I added delay(1) as you suggested and sure enough, it printed! And yep, it crashed right after.

    So the problem is the NULL pointers - I thought this was standard practice for continuing to use the strtok() function after passing it a string?

    This code does work multiple times on the UNO - I can send "CHAN," "READ," etc without causing a crash (or perhaps the UNO crashes and then recovers without me noticing?). Is it the difference in USB communication method that allows it to work on the UNO? I'm not familiar with the behavior of USB asynchronous vs serial.

  7. #7
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    Quote Originally Posted by Doodads View Post
    This code does work multiple times on the UNO
    That's indeed amazing. I don't know why. It seems not to care about null-pointers.

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,422
    <edit>: I agree this should be provided in the core with a way to activate it for display on boot. Working on this in prior Beta's (with debug_tt) it seemed like it would be useful - but there were so few reports of FAULT like behavior - and while trapping the error could often still print to USB before it hung - it was not as elegant a solution as storing the info in 'static' RAM and displaying it on restart when the Teensy returned to full function in all cases! And providing an external library and needing to connect it was non-trivial.

    Paul added code mem exception control to enforce NULL pointer failure on T_4.x's. Before that Teensy 4.x would not care about NULL pointer use either. It was up to the code at hand to 'suffer the consequences' There were a few odd 'HANGS' when that was put in that had been working before.

    It may work on the UNO because it is 'ignored' and until the NULL pointer abuse causes lasting functional damage it continues to 'appear to be running well'.

    One other useful note KurtE drops with regard to debugging is when a spot in code is suspected as the failure point and no SerMon output appears add : Serial.flush() after the Serial.print("debug this note") which in place of the delay(1) - blocks the code until the output is transmitted. If it doesn't show then - it is failing or 'damaged' before that spot.

    If this is the right stuff - also Frank - looks like you could test fault code working against the second - Stack OverFlow? {from T4's startup.c}::
    Code:
    ...
    	// TODO: trap regions should be created last, because the hardware gives
    	//  priority to the higher number ones.
    	SCB_MPU_RBAR = 0x00000000 | REGION(i++); // trap NULL pointer deref
    	SCB_MPU_RASR =  DEV_NOCACHE | NOACCESS | SIZE_32B;
    ...	
    	SCB_MPU_RBAR = ((uint32_t)&_ebss) | REGION(i++); // trap stack overflow
    	SCB_MPU_RASR = SCB_MPU_RASR_TEX(0) | NOACCESS | NOEXEC | SIZE_32B;
    ...
    And this added to .ld file:
    Code:
    	.text.itcm : {
    		. = . + 32; /* MPU to trap NULL pointer deref */
    ...

  9. #9
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    Well it detects this:
    Code:
    #include <T4_PowerButton.h>
    extern unsigned long _ebss;
    void setup() {
      uint32_t *p = ((uint32_t)&_ebss);
      *p = 0xdeadbeef;
    }
    
    void loop() {}
    and prints
    Code:
    Hardfault.
    Return Address: 0x166
        (DACCVIOL) Data Access Violation
        (MMARVALID) Accessed Address: 0x20002540
    I testet a stack overflow with recursive calls, hm, does not work. Don't know why, at the moment.. any idea?

  10. #10
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,422
    Quote Originally Posted by Frank B View Post
    Well it detects this:
    ...

    I testet a stack overflow with recursive calls, hm, does not work. Don't know why, at the moment.. any idea?
    Interesting that is failing to trigger on code over filling the stack? It is a 32 Byte(?) region? Maybe the recursive code isn't reading|writing to it to trigger but just stepping over it?

    Have not gotten to test the added Fault detect code yet - but did glance at that example though didn't look close enough with this in mind.

  11. #11
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    Had an other idea. Switched to "optimize: debug", and voilá, it prints the error.

    Hm it works if I switch to optimize "debug"
    Code:
    #include <T4_PowerButton.h>
    
    void setup() {
      setup();
    }
    
    void loop() {}
    So, GCC seems to do something weired.

  12. #12
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,422
    Interesting - well it is an otherwise empty function - may it gets optimized away ???

    Could do a normal non-debug build and add a static var and use a stack var to get it and change it in some way it doesn't just optimize that way and edit the static directly. Or maybe call another function with a return value in setup before calling setup?

    Does it get to loop() ?

  13. #13
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    Found it. GCC is smarter than me
    Without optimization it produces this ("bl")
    With optimization it just branches ("b") to the start of the function - id does not call. So, correct - there is no stack overflow..
    The example code is just too simple.

  14. #14
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    Right. This crashes, as expected:
    Code:
    #include <T4_PowerButton.h>
    
    void setup() {
      volatile char c = 42;
      setup();
    }
    
    void loop() {}

  15. #15
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    I'll create a PR for Paul... not sure if he wants to merge it.. This certainly needs some advocates

  16. #16
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,422
    Quote Originally Posted by Frank B View Post
    I'll create a PR for Paul... not sure if he wants to merge it.. This certainly needs some advocates
    As long as there is a clean and easy way to include or enable it it sells itself - having a lib in the CORES would be awesome to just add #include 'something'. As noted I started working on it in T_3.6 beta I think - revised it for T_4.0 after finding that noted 'naked' stack dump function but never saw the path for printing reliably. And even trying to keep it simple it wasn't simple or clean enough, or used often enough that I stuck with it - or got back to it.

    Paul commented in T_4.0 he expected to revisit it to get better info and blinking - that was before I added that 'naked' code that solved the stack dump better than what was there originally - and then it didn't get revisited.

    The GDB debugger is nice, but some real effort to get right and get connected for usage between build and then SerMon/Terminal connection setup and command line.

    This isn't real time debugging - but AFTER a FAULT - there isn't much chance of normal function at times and nothing you can do about it except restart. And if info can be saved at fault time and shown on restart - that is the best you can do.

    > It might be interesting to have the default OVER TEMP code feed this - does it? Once it goes PANIC there is no chance it seems - but the step before that could be logged and shown on restart.

    > Also perhaps recording info like F_CPU_ACTUAL, millis() at Fault, and other relevant easy to gather notes that might explain the trouble or be useful::
    >> It runs for 5 minutes and dies, or it dies at 200 or 400 millis().
    >> Or the one T_4.0 I BURNED UP because I was building on a fresh T_4.0 that kept 'failing to work' because the IDE was still set to high overclocking speed with heat sync required when it did not have one and I didn't notice until after the damage was done after building and rebuilding thinking the trouble was in the i2c display library I was adding.

  17. #17
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,921
    Quote Originally Posted by defragster View Post
    > It might be interesting to have the default OVER TEMP code feed this - does it
    Hmm, I don't think so.
    Let me think about how to add this....
    It would be a good feature. Possibly, in this case, it should also lower the speed of the CPU to give it a chance to cool down.

  18. #18
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,422
    Quote Originally Posted by Frank B View Post
    Hmm, I don't think so.
    Let me think about how to add this....
    Indeed maybe not ... I think you'd have to hook the warn callback and make a note of that in the static RAM. Then if it went OVERTEMP and restarted - not sure if that is a reported FAULT? If it is reported then it is already displayed - if not then on restart it could be posted as a warning that may or may not have been the cause of the restart.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •