Teensy 4.0 compiler issue? Maybe?

Status
Not open for further replies.

Tarnarmour

New member
I'm using a Teensy 4.0 to read some data from a pair of flow sensors. The code I've written here is hanging in the setup function, but not at a specific function. It hangs at whatever the last function to actually execute in the setup function is. IE, if I try to tell the sensors to reset, then read some data off them, it will hang at the last actual function call in that process. If I stick some instructions to set some bits high or low AFTER that, then the pin high or low thing will hang. Whatever is last in the setup function (as long as it actually compiles, if I just set some variables that aren't used anywhere and the compiler catches it then they will not be the last function to be called) hangs.

I'm pretty baffled. I've only got moderate Arduino experience so it could definitely just be a mistake somewhere obvious on my side. In any case I'm looking forward to the explanation for this one!

Code:
#include <Wire.h>

const int FLOW1PWR = 22; // digital IO pin to flow 1 mosfet, low for on, high for off
const int FLOW2PWR = 23; // digital IO pin to flow 2 mosfet, low for on, high for off
const int PRESSUREPIN = 1; // Analog input pin for pressure sensor

const uint8_t addr1 = 0x2E;
const uint8_t addr2 = 0x2E;
const uint8_t commandMeasure[2] = {0x36, 0x08}; // command for the new sensor to start measurements
const uint8_t commandAverage[2] = {0x36, 0x66}; // command for the new sensor to change averaging mode. The following command should be the number of samples to average, with 0 being average until read
const uint8_t commandReset[2] = {0x00, 0x06}; // command to reset the new sensor (0x00 is the address, one byte message?)
const float scaleFactor = 170; // scale factor for new sensor
const float sensorOffset = -24576; // offset for the new sensor. New sensor transmits data as a 2's compliment number
const float VDD = 5.0;

float flow1Topic = 0;
float flow2Topic = 0;
float pressureTopic = 0;

bool verb = false; // true to print data every loop, false to print when queried

void setup() 
{
  PinSetup();
  
  Wire.begin();
  Wire1.begin();
  Serial.begin(9600);
  while(!Serial){ }

  Serial.println("Begin");

  delay(5); // see SFM3019 datasheet table 2.4 for reset timing requirements

  Serial.print("-1 ");
  WireWrite(commandReset,addr1,0); // send reset to sensor 1
  Serial.print("-1.1 ");
  WireWrite(commandReset,addr2,1); // send reset to sensor 2

  delay(3);
  Serial.print("-2 ");
  WireWrite(commandMeasure,addr1,0); // start measurement command
  Serial.print("-2.1 ");
  WireWrite(commandMeasure,addr2,1); // start measurement command
  Serial.print("-3 ");


    FlowOnePwr(false);
    FlowTwoPwr(false);
    Serial.print("-4 "); // currently this is the last thing it executes, it hangs on the delay function. If I get rid of the delay function it will hang on the FlowTwoPwr() call.
    delay(10);
    PinSetup();
    Serial.print("-5 ");
}

void loop()
{
  Serial.println("loop");
  pressureTopic = GetPressure();
  
  int flow1state = WireWrite(0, addr1, 0); // check status of sensors by sending empty message and checking for NACK
  int flow2state = WireWrite(0, addr2, 1);

  if (flow1state == 2 || flow1state == 4)
  {
    if (flow2state == 2 || flow2state == 4)
    {
      ResetSensor(3);
    }
    else
    {
      ResetSensor(1);
    }
  }
  else if (flow2state == 2 || flow2state == 4)
  {
    ResetSensor(2);
  }
  
  byte data[3] = {0, 0, 0xFF};

  Serial.println("check3");

  if (flow1state == 0)
  {
    WireRead(addr1,3,0,data);
    if (CrcCheck(data) == 0)
    {
      int holder = ((data[0] << 8) + data[1]);
      holder = -(holder & 32768) + (holder & 32767); // 2's complement from the bytes
      flow1Topic = (holder - sensorOffset) / scaleFactor;
    }
  }

  data[0] = 0; // clear data between reading in case read fails
  data[1] = 0;
  data[2] = 0xFF;

  if (flow2state == 0)
  {
    WireRead(addr2,3,1,data);
    if (CrcCheck(data) == 0)
    {
      int holder = ((data[0] << 8) + data[1]);
      holder = -(holder & 32768) + (holder & 32767); // 2's complement from the bytes
      flow2Topic = (holder - sensorOffset) / scaleFactor;
    }
  }
  
  if (Serial.available())
  {
    char inChar = Serial.read();
      switch(inChar)
      {
        case 'D':
          Serial.print("d");
          Serial.print(flow1Topic); Serial.print(" ");
          Serial.print(flow2Topic); Serial.print(" ");
          Serial.print(pressureTopic); Serial.print(" ");
          Serial.print(flow1state); Serial.print(" "); Serial.println(flow2state);
          break;

        case 'Q':
          Serial.println("r");
          break;
        case 'V':
          verb = !verb;
          if (verb)
          {
            Serial.println("verbose");
          }
          else
          {
            Serial.println("quiet");
          }
        default:
          Serial.println("e");
      }
  }
  else if (verb)
  {
    Serial.print("Flow Sensor 1: "); Serial.println(flow1Topic);
    Serial.print("Flow Sensor 2: "); Serial.println(flow2Topic);
    Serial.print("Pressure Sensor: "); Serial.println(pressureTopic);
    delay(10);
  }
}

// *******************************************************************************************************

void PinSetup()
{
  pinMode(FLOW1PWR, OUTPUT);
  pinMode(FLOW2PWR, OUTPUT);
  pinMode(PRESSUREPIN, INPUT);
  digitalWrite(FLOW1PWR, LOW);
  digitalWrite(FLOW2PWR, LOW);
}

int WireWrite(uint8_t command[], uint8_t ADDR, int I2CBUS)
{
  int command_length = sizeof(command) / sizeof(command[0]);
  if (I2CBUS == 0)
  {
    Wire.beginTransmission(ADDR);
    for (int i = 0; i < command_length; i++)
    {
      Wire.write(command[i]);
    }
    return Wire.endTransmission(false);
  }
  else
  {
    Serial.print("-a.1 ");
    Wire1.beginTransmission(ADDR);
    for (int i = 0; i < command_length; i++)
    {
      Wire1.write(command[i]);
    }
    Serial.print("-a.2 ");
    return Wire1.endTransmission(false);
  }
}

void WireRead(uint8_t ADDR, int read_length, int I2CBUS, byte data[])
{
  Serial.println("-a.1 ");
  if (I2CBUS == 1)
  {
    Serial.println("-b.1 ");
    Wire1.requestFrom(ADDR, read_length, false);
    for (int i = 0; i < read_length; i++)
    {
      Serial.println("-b.11 ");
      data[i] = Wire1.read();
    }
    Serial.println("-b.2 ");
  }
  else
  {
    Serial.println("-c.1 ");
    Wire.requestFrom(ADDR, read_length, false);
    for (int i = 0; i < read_length; i++)
    {
      data[i] = Wire.read();
    }
    Serial.println("-c.2 ");
  }
  Serial.println("-a.2 ");
}

byte CrcCheck(byte data[3])
{
  byte byteCtr; byte calc_crc = 0;
  for (byteCtr = 0; byteCtr < 3; ++byteCtr) {
      calc_crc ^= (data[byteCtr]);
      for (int i = 8; i > 0; --i) {
          if (calc_crc & 0x80) { calc_crc = (calc_crc << 1) ^ 0x131; }
          else { calc_crc = (calc_crc << 1); }
      }
  }
  return calc_crc;
}

float GetPressure()
{
  float sensorVal = analogRead(PRESSUREPIN);
  return ((((sensorVal * VDD / 1023.0 - VDD/2) - VDD / 10) * (120) / (0.8 * VDD) + 60) * 1.02) - 87.5;
}

void FlowOnePwr(bool onoff)
{
  if (onoff)
  {
    digitalWrite(FLOW1PWR, LOW);
  }
  else
  {
    digitalWrite(FLOW1PWR, HIGH);
  }
}

void FlowTwoPwr(bool onoff)
{
  if (onoff)
  {
    digitalWrite(FLOW2PWR, LOW);
  }
  else
  {
    digitalWrite(FLOW2PWR, HIGH);
  }
}

void ResetSensor(int option) // 1 for sensor 1, 2 for sensor 2, 3 for both
{
  
  uint8_t data_holder[3];
  switch (option)
  {
    case 1:
      FlowOnePwr(false);
      delay(5);
      FlowOnePwr(true);
      delay(2);
      WireWrite(commandReset,addr1,0);
      delay(2);
      WireWrite(commandMeasure,addr1,0);
      WireRead(addr1,3,0,data_holder);
      WireRead(addr1,3,0,data_holder);
      break;
    case 2:
      FlowTwoPwr(false);
      delay(5);
      FlowTwoPwr(true);
      delay(2);
      WireWrite(commandReset,addr2,1);
      delay(2);
      WireWrite(commandMeasure,addr2,1);
      WireRead(addr2,3,1,data_holder);
      WireRead(addr2,3,1,data_holder);
      break;
    case 3:
      FlowOnePwr(false);
      FlowTwoPwr(false);
      delay(5);
      FlowOnePwr(true);
      FlowTwoPwr(true);
      delay(2);
      WireWrite(commandReset,addr1,0);
      WireWrite(commandReset,addr2,1);
      delay(2);
      WireWrite(commandMeasure,addr1,0);
      WireWrite(commandMeasure,addr2,1);
      WireRead(addr1,3,0,data_holder);
      WireRead(addr2,3,1,data_holder);
      WireRead(addr1,3,0,data_holder);
      WireRead(addr2,3,1,data_holder);
      break;
  }
}
 
I briefly looked over your code.

This
Code:
int WireWrite(uint8_t command[], uint8_t ADDR, int I2CBUS)
{
  int command_length = [B][COLOR="#B22222"]sizeof(command)[/COLOR][/B] / sizeof(command[0]);
  if (I2CBUS == 0)
  {
    Wire.beginTransmission(ADDR);
....

might or might not be the reason. In any case it is a bug. If you pass an array to a function the array parameter decays into a pointer. So, sizeof(command) will always be 4 (the size of a pointer). If you need the size of the array in the function you need to pass it to the function.

Code:
int WireWrite(uint8_t command[],[COLOR="#B22222"][B] size_t command_length[/B][/COLOR],  uint8_t ADDR, int I2CBUS)
{
  ...
}
 
The usual Wire protocol for Wire read is after you issue Wire.requestFrom() you then use Wire.available() before Wire.read(). maybe look at some Arduino I2C examples ....
 
The usual Wire protocol for Wire read is after you issue Wire.requestFrom() you then use Wire.available() before Wire.read(). maybe look at some Arduino I2C examples ....

I am aware of that, as I mentioned in the post the issue here is almost certainly not in the Wire library functions. It will hang on whatever the last instruction is. I can go back and make the Wire protocol better written later if I run into issues, right now I just want to figure out why the program is hanging in the setup function.
 
Put the while(!serial) before wire.begin

And maybe a delay(1000) to test

Teensy starts fast and device may not be ready
 
Whatever is last in the setup function (as long as it actually compiles, if I just set some variables that aren't used anywhere and the compiler catches it then they will not be the last function to be called) hangs.

Crashing when a function ends and tries to return to whatever called it can be a sign something has overwritten data on the stack, which includes the return address.

I copied your code into Arduino and clicked Verify. It generates many compiler warnings. Most can be solved if you just add "const" to WireWrite, like this:

Code:
int WireWrite(const uint8_t command[], uint8_t ADDR, int I2CBUS)

Of the remaining warnings, this sort of thing probably deserves your attention first:

Code:
sketch_nov04a:162: warning: 'sizeof' on array function parameter 'command' will return size of 'const uint8_t* {aka const unsigned char*}' 
   int command_length = sizeof(command) / sizeof(command[0]);

Maybe there's an error in computing "command_length"?


I will admit, many compiler warnings are harmless and annoying. But many others try to call your attention to common pitfalls. I know it takes time to go through your program to clean up the minor issues causing so many warnings, but I believe it is time well spent. Sometimes those warnings turn out to be real problems. Let the compiler help you to improve your code.
 
Your sketch actually runs (not connected to anything) on a T3.2. It will also run on T4.0 (nothing connected) if you correct the fatal flaw
Code:
  int flow1state = WireWrite(0, addr1, 0); // check status of sensors by sending empty message and checking for NACK
  int flow2state = WireWrite(0, addr2, 1);
WireWrite expects a byte array as first argument, you are passing a constant. A hack fix would be
Code:
const uint8_t zero[] = {0,0};
...
  int flow1state = WireWrite(zero, addr1, 0); // check status of sensors by sending empty message and checking for NACK
  int flow2state = WireWrite(zero, addr2, 1);

As several folks have noted, your command_length calculation is wrong. Since your command arrays are 2 bytes, maybe just use command_length = 2;

Wire.requestFrom() returns the number of bytes available for reading (as does Wire.available()). You should use the return value from one of those to decide how many Wire.read()'s you can do...

It's good practice to check the return values from the various Wire functions.

Teensy 4 is NOT 5v tolerant, so sensor needs to run at 3.3v with pullup resistors (to 3.3v) on SCL and SDA pins
 
Last edited:
Status
Not open for further replies.
Back
Top