Teensy stops responding after some time in outdoor installation

Status
Not open for further replies.

alexandros

Well-known member
I'm setting up an outdoor installation with lots of alarm lamps (the ones that spin around, like those on ambulances). and I'm running into all sorts of issues. I'm using PIR sensors which trigger a row of lamps (split in two parts, which fire together). The idea is that when someone walks by, the lamps start dimming in one after the other in two rows, simultaneously, and when all are light up, they dim out again one by one. My main issue is that lots of Teensies (they are 13 Teensy LC in total) just stop responding after some time. I know that the sensors do need some time without any movement when powered up in order to get a picture of the environment they need to track and that's not really the case as it is a public space and I can't know that no one will pass when the installation boots up.

Here's the code for one of the Teensies.
Code:
#define RAMPTIME 1000
#define LAMPDEL 1000
#define RAMPHOLD 6000

#define TABSIZE 256

// maximum number of lamps in a row
#define NUMLAMPS 7

#define LOWVAL 50
#define HIGHVAL 255


/////////////////// PWM VARIABLES /////////////////
int pwmTab[TABSIZE];

unsigned long runDur = LAMPDEL * NUMLAMPS;

bool rampStarted = false;
int rampTarget = 0;
int lastTarget = LOWVAL;
int rampVals[2][NUMLAMPS];
int previousLamp[2] = {0, 0};
int numSteps = 0;
unsigned long stepDur = 0;
unsigned long curStepTimeStamps[2][NUMLAMPS] = { 0 };
unsigned long lampTimeStamps[2][NUMLAMPS] = { 0 };
unsigned long rampStartTimeStamp = 0;
unsigned long rampEndTimeStamp = 0;
bool lampsRunning[2][NUMLAMPS] = { false };
bool targetReached[2][NUMLAMPS] = { false };
int stepDir = 1;
bool waitingToEndRamp = false;

int pwmPins[2][NUMLAMPS] = {{3, 4, 6, 9}, {10, 16, 17, 20, 22, 23}};
int howManyLampsOnRow[2] = {4, NUMLAMPS};

//////////////// SENSOR VARIABLES /////////////////
int pirPin = 2;
int oldPirVal = 0;

void setup() {
  for (int i = 0; i < TABSIZE; i++) {
    pwmTab[i] = (float)pow(((float)i/(float)TABSIZE), 2) * TABSIZE;
  }
  
  for (int row = 0; row < 2; row++) {
    for (int i = 0; i < NUMLAMPS; i++) {
      pinMode(pwmPins[row][i], OUTPUT);
      analogWrite(pwmPins[row][i], pwmTab[LOWVAL]);
      rampVals[row][i] = LOWVAL;
    }
  }

  pinMode(pirPin, INPUT);
  delay(60000);
}

void loop() {
  checkSensor();
  
  if (rampStarted) runRamp();
  else if (waitingToEndRamp) {
    if ((millis() - rampEndTimeStamp) > RAMPHOLD) {
      endRamp();
    }
  }
}

void checkSensor() {
  int pirVal = digitalRead(pirPin);
  if (pirVal != oldPirVal) {
    if (pirVal) {
      if (!rampStarted && !waitingToEndRamp) {
        startRamp();
      }
    }
    oldPirVal = pirVal;
  }
}

void calcSteps() {
  numSteps = abs(rampTarget - lastTarget);
  if (rampTarget > lastTarget) stepDir = 1;
  else stepDir = -1;
  stepDur = RAMPTIME / numSteps;
}

void startRamp() {
  rampStarted = true;
  rampTarget = HIGHVAL;
  calcSteps();
  rampStartTimeStamp = millis();
}

void endRamp() {
  if ((millis() - rampStartTimeStamp) > runDur) {
    rampStarted = true;
    rampTarget = LOWVAL;
    calcSteps();
  }
}

void runRamp() {
  for (int row = 0; row < 2; row++) {
    bool foundInactiveLamp = false;
    for (int i = 0; i < howManyLampsOnRow[row]; i++) {
      if (!lampsRunning[row][i]) {
        if (!i) {
          lampTimeStamps[row][i] = millis();
          lampsRunning[row][i] = true;
          previousLamp[row] = i;
        }
        else {
          if ((millis() - lampTimeStamps[row][previousLamp[row]]) > LAMPDEL) {
            lampTimeStamps[row][i] = millis();
            lampsRunning[row][i] = true;
            previousLamp[row] = i;
          }
        }
      }
      if (foundInactiveLamp) {
        break;
      }
    }
  }
  for (int row = 0; row < 2; row++) {
    for (int i = 0; i < howManyLampsOnRow[row]; i++) {
      if (lampsRunning[row][i]) {
        if ((millis() - curStepTimeStamps[row][i]) > stepDur) {
          if (!targetReached[row][i]) {
            rampVals[row][i] += stepDir;
            for (int j = 0; j < howManyPinsToChangePWMFreq; j++) {
              analogWrite(pwmPins[row][i], pwmTab[rampVals[row][i]]);
            }
            curStepTimeStamps[row][i] = millis();
          }
        }
        if (rampVals[row][i] == rampTarget) {
          targetReached[row][i] = true;
          if ((i == (howManyLampsOnRow[row]-1)) && (row == 1)) {
            rampStarted = false;
            lastTarget = rampTarget;
            for (int j = 0; j < 2; j++) {
              for (int k = 0; k < howManyLampsOnRow[j]; k++) {
                lampsRunning[j][k] = false;
                targetReached[j][k] = false;
              }
            }
            if (rampTarget == HIGHVAL) {
              waitingToEndRamp = true;
              rampEndTimeStamp = millis();
            }
            else waitingToEndRamp = false;
          }
        }
      }
    }
  }
}

To dim the lamps I'm using this MOSFET module. The Teensy connects to the ground and PWM pins of the module, and the blue clamp of the module connects to the power supply. The lamps has its positive input connecting to the power supply positive, and its negative to the LOAD output of the MOSFET module. Things start OK, but after a while many Teensies seem to stop reacting.

My question is, does anyone think there is something wrong with the code or connections mentioned above?
I should mention that the environment is quite humid, and all the Teensies and MOSFET modules are enclosed in boxes like this (sorry, the page is in Greek). Whenever I reload the code the Teensies seem to work, but at some random point problems start to occur.
 
Code:
// maximum number of lamps in a row
#define NUMLAMPS 7
......................................
int pwmPins[2][NUMLAMPS] = {{3, 4, 6, 9}, {10, 16, 17, 20, 22, 23}};
int howManyLampsOnRow[2] = {4, NUMLAMPS};

I find the code hard to follow but one thing I notice is that you defined 6 pins on the 2nd row, but in howManyLampsOnRow you say there are 7 pins ( NUMLAMPS is 7 ). So it would seem you may be pulse width modulating pin 0 at some point. I guess this could interfere with Serial reads( edit, that's on Serial1 ) , but may not be your actual problem.
 
I agree with rcarr that having NUMLAMPS = 7 and incomplete initialization of the pwmPins array is a problem waiting to happen.

I wrote a short sketch to check out the values in the pwmPins array and got these results:


Row 0: 3 4 6 9 0 0 0
Row 1: 10 16 17 20 22 23 0

You should correct your array sizes and pin allocations, then try an extended test in an indoor setting where you can monitor power supply voltages. Your outdoor location may have longer power leads and be more prone to power supply issues.

Since the Teensy LC has only 10 PWM pins, you might be able to simplify your hardware and software by having the lamps in the two rows that fire together connected to the same Teensy pin. You don't have enough pins to handle two rows of 7 lamps----or even two rows of 6 lamps---if each lamp has to connect to a unique Teensy pin.

Another possibility is that there are leakage currents in the MOSFET modules that are trying to drive the Teensy pins above their 3.3V tolerance. I would put a resistor of about 1K Ohms between the Teensy pin and the MOSFET gate.

My main issue is that lots of Teensies (they are 13 Teensy LC in total) just stop responding after some time.

When you say "after some time" I presume that you mean an interval less than 49.7 days. After that time, when the millis() counter overflows, your time comparisons are going to have problems. You should study up on the use of the elapsedMillis() object, which handles the timer overflow for you.
 
Actually the code I posted was not 100% accurate. I should have put seven values there as I do in the actual project, but missed that here. The values of the longer array are: 4, 22, 3, 10, 9, 6, 16.
There is also this line:
Code:
howManyLampsOnRow[2] = {4, NUMLAMPS};
which is tested in the runRamp() function, and ensures that no additional pins will be used. @mborgerson you might be right as to the number of PWM pins. In the case of the code I posted, the second PWM pin of the longer array is not used in hardware (that lamp is actually missing from the installation), but the intention is to act as if it was there. Now that I see the code clearer, this value is shared between the two arrays, and that's something I should look at. Still, there's no pin that can't do PWM that is called with analogWrite(), I'm pretty sure of that.

The installation goes off every evening, so millis() is not a problem here.
 
Sorry for coming back after so long, was so busy with this thing I forgot about this thread I started. Eventually it seems the problem lies in the power supply which get problematic due to humidity or even water (it has rained a lot the last days and the installation is in a very humid place). Apparently power goes on and off at certain points forcing the Teensies to reboot. Code and circuit are most likely fine.
Thanks for the hints anyway!
 
Status
Not open for further replies.
Back
Top