Help with code...

Hey y'all! New here and new to Arduino and C++, or Arduino's abstraction of it rather. Not new to automation and general programming concepts however, as I am a PLC programmer by trade so I do understand bits, bytes, arrays, etc.

So, I have an Arduino Uno (actually an Elegoo Uno R3) which I am using to control the LED's in one of those fake fireplaces found in entertainment centers/TV stands. The only way I have available to determine when to turn on/off the LED light show is by observing the rotation of the motor that generates the motion effect of the flames. No worries, because I've got an optical detector to sense rotation of said motor, and it outputs +5VDC or 0VDC depending on state, so it's easily monitored via digital input. When the motor is running (which is when I want the LED's to be on) the output of the sensor will alternate regularly between +5V and 0V. When the motor is stopped (which is when I'll want to switch the LED's off) the sensor output will stop alternating and stay in one of the two possible states.

What I need help with is reading the state of the sensor output (high or low), waiting a set amount of time, reading it again then setting a status bit to determine if the motor is stopped or running. Has anyone done this before? I've tried several variations of nested "if" statements using millis() for timing control, but have run into several problems including the timer(s) not timing at regular intervals! Perhaps a library conflict?

I'd be happy to share my code ("sketch") when I get back to my computer, but I figured I'd see if anyone had any basic ideas first.

Thanks!

What is the pulse rate when the motor is running? I would use the pulses to reset a millis() timer, if the pulses stopped and the timer expired, take action.

dixie460:
What I need help with is reading the state of the sensor output (high or low), waiting a set amount of time, reading it again then setting a status bit to determine if the motor is stopped or running.

If the pulses are fast you probably need to use an interrupt to detect them - otherwise there is a risk that you accidentally sample the signal every time it happens to be LOW (or HIGH) and incorrectly assume it is stopped.

I use this code to control the speed of a motor. You could simplify it for your situation. If the interval between pulses exceeds some value you know the motor is stopped.

const byte fwdPin = 9;
const byte revPin = 10;
const byte potPin = A1;

int potVal;
int pwmVal;

unsigned long revMicros;
unsigned long prevRevMicros;
unsigned long revDuration;
unsigned long revCount;

unsigned long prevDisplayMillis;
unsigned long  displayInterval = 1000;

    // variables for the ISR
volatile unsigned long isrMicros;
volatile unsigned long isrCount;
volatile bool newIsrMicros = false;



void setup() {
    Serial.begin(115200);
    Serial.println("SimpleISRdemo.ino");
    pinMode (fwdPin, OUTPUT);
    pinMode (revPin, OUTPUT);

    isrCount = 0;
    attachInterrupt(0, revDetectorISR, RISING);
}

//==========

void loop() {
    getIsrData();
    if (millis() - prevDisplayMillis >= displayInterval) {
        prevDisplayMillis += displayInterval;
        showData();
        readPot();
        updateMotorSpeed();
    }
}

//===========

 void readPot() {
    potVal = analogRead(potPin);
}

//===========

void updateMotorSpeed() {
    pwmVal = potVal >> 2;

    digitalWrite(revPin,LOW);
    analogWrite(fwdPin, pwmVal);
 }

//===========

void getIsrData() {
    if (newIsrMicros == true) {
        prevRevMicros = revMicros; // save the previous value
        noInterrupts();
            revMicros = isrMicros;
            revCount = isrCount;
            newIsrMicros = false;
        interrupts();
        revDuration = revMicros - prevRevMicros;
    }
}

//===========

void showData() {
    Serial.println();
    Serial.println("===============");
    Serial.print("PWM Val "); Serial.println(pwmVal);
    Serial.print("  Rev Duration ");
    Serial.print(revDuration);
    Serial.print("  Rev Count ");
    Serial.print(revCount);
    Serial.println();
}

//===========

void revDetectorISR() {
    isrMicros = micros();
    isrCount ++;
    newIsrMicros = true;

}

...R

Outsider, I considered the fact that I might want to use an interrupt, however the motor only turns approximately 20 RPM. Do you still think it may be necessary? As for using a millis()-based timer, that's exactly what I've tried to do.

I've included the code I have currently, the timers in question are the first two that you'll see, the rest of the code seems to work fine. However I do need to clean it up some after I get everything working. Feel free to tear it apart and school me on better methods. I won't be offended :slight_smile: because as I said, I'm new to this programming language.

The trouble I'm having with this code is that although it does recognize the state change on digital input 2 when monitoring the two bits "photoStalledLow" and "photoStalledHigh" using the serial print lines at the end of the program (currently commented out), the stall detection timers don't seem to be keeping time as shown by the serial print lines within each timer. This causes the LED's to randomly turn off before resuming normal operation. Probably a conflict somewhere but I'm not quite sure where to look, as each individual section of program will execute fine all on it's own. Only when I combine into this complete program them do I experience the problem.

//-------------------------------------------------------------------------------------------------------------------
// Fireplace lighting control program. Randomizes brightness of flame effect & gradually varies brightness of coal bed & logs.
//-------------------------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------------------------
// Hardware:
// Elegoo Uno R3 - Main Controller (ATMega 328P @ 16 MHz, 5VDC logic level)
// Adafruit PCA9685 16-Channel 12-Bit PWM Servo Driver - PWM Output Module (I2C bus address 0x40)
//-------------------------------------------------------------------------------------------------------------------


//-------------------------------------------------------------------------------------------------------------------
// Include libraries for I2C comms, PCA9685 PWM driver module and Entropy library (Entropy generates random numbers).
//-------------------------------------------------------------------------------------------------------------------

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <Entropy.h>



//-------------------------------------------------------------------------------------------------------------------
// Define "pwm" and I2C bus address of PCA9685.
// Declare global variables necessary for program control.
//-------------------------------------------------------------------------------------------------------------------

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

bool photoStalledLow;
bool photoStalledHigh;
unsigned long stallTimerL;
unsigned long stallTimerH;


//-------------------------------------------------------------------------------------------------------------------
// Begin setup routine.
//-------------------------------------------------------------------------------------------------------------------
   
      // Start serial comms @ 9600 baud.
      // Initialize Entropy library.
      // Start PWM.
      // Set PWM frequency.
      // Set I2C bus speed. If Wire.setClock() is commented out, bus defaults to 100 kHz.
      // Initialize digital input 2 with internal pullup for testing, to be changed to input with external pulldown resistor in final build.
      
void setup() {
  Serial.begin(9600);
  Entropy.initialize();
  pwm.begin();
  pwm.setPWMFreq(500);
  //Wire.setClock(400000);
  pinMode(2, INPUT_PULLUP);
}


  
//-------------------------------------------------------------------------------------------------------------------
// Begin main routine.
//-------------------------------------------------------------------------------------------------------------------

void loop() {
     
       // Stalled/stopped motor detection.
      //  If photointerrupter reads LOW or HIGH for more than n milliseconds, set appropriate "Stalled" bit.
      //  These bits are used later in the program to enable/disable the flame effect.

if (digitalRead(2)==LOW){
  signed int photo_TL = 1000;
    if (millis() - stallTimerL >= (photo_TL)) {
      stallTimerL += millis();
      Serial.println("Timer Low");
      Serial.println(stallTimerL);
      photoStalledLow=1;
    }
}
    else photoStalledLow=0;

    
      if (digitalRead(2)==HIGH){
  signed int photo_TH = 1000;
    if (millis() - stallTimerH >= (photo_TH)) {
      stallTimerH += millis();
      Serial.println("Timer High");
      Serial.println(stallTimerH);
      photoStalledHigh=1;
    }
}   
    else photoStalledHigh=0;

      // If motor is not stalled or stopped, enable LEDs.
              
    if ((photoStalledLow==0) && (photoStalledHigh==0)) {
  
      // Flame control. Randomize brightness of flame effects one half of the flame LED board at a time.
      // Entropy format is Entropy.random(min_value, max_value)
      // PWM command format is pwm.setPWM(channel, min_pulse_width, max_pulse_width)
      // Minimum PWM value is 0, maximum is 4095.

int randFlame1 = Entropy.random(0,4000);
int randFlame2 = Entropy.random(0,4000);

      pwm.setPWM(0, 0, (randFlame1));
      pwm.setPWM(1, 0, (randFlame1));
      pwm.setPWM(2, 0, (randFlame1));
      pwm.setPWM(3, 0, (randFlame2));
      pwm.setPWM(4, 0, (randFlame2));
      pwm.setPWM(5, 0, (randFlame2));
  
      // Coal bed control. Increment/decrement brightness of coal bed and logs.
      // Entropy format is Entropy.random(max_value) OR Entropy.random(min_value, max_value)
      // PWM format is pwm.setPWM(channel, min_pulse_width, max_pulse_width)
      // Minimum PWM value is 0, maximum is 4095.
      
            // Explanation: Entropy returns a random value between 0 and 5000 to be used as timer preset, therefore
            // timer times out at random invervals between 0 and 5 seconds. When timer times out, add or
            // subtract "1" from "i", which is the output to the setPWM command. Variables used are:
            // _Rand: Timer preset
            // _ETimer: holds the value of millis() from the previous scan
            // i: output value to setPWM command
            // v: added to "i" to increment or decrement it. Value of this variable is either 1 or -1.

signed long _random = Entropy.random(5000);
static unsigned long randTimer;
  if (millis() - randTimer >= (_random)) {
    randTimer += (_random);
    static unsigned int i=2000;
    static signed int v=1;
    
    if (i < 4000) {
      if (v == 1){
        i+=v;
      }
    }
    if (i==4000) {
      v=-1;}

    if (i <= 4000) {
      if (v == -1) {
        i+=v;
      }
    }
    if (i==350) {
      v=1;}
      pwm.setPWM(6, 0, i);
      pwm.setPWM(7, 0, i);
      pwm.setPWM(8, 0, i);    //ch 8 dead for some reason, investigate later.
      pwm.setPWM(9, 0, i);
      pwm.setPWM(10, 0, i);
      pwm.setPWM(11, 0, i);
    }
  }

            //Disable flame effect LED's if motor is stalled/stopped with photodetector either low or high.
            //LED's are disabled by setting PWM values to zero for all channels of the PCA9685 module.
            //Perhaps clean this up later and use an array or something.
              
  if ((photoStalledLow==1)||(photoStalledHigh==1)) {
  unsigned long led_Off_Delay = 1000;
  static unsigned long ledOffT = millis();
    if (millis() - ledOffT >= (led_Off_Delay)) {
      ledOffT += (led_Off_Delay);
    pwm.setPWM(0, 0, 0);
    pwm.setPWM(1, 0, 0);
    pwm.setPWM(2, 0, 0);
    pwm.setPWM(3, 0, 0);
    pwm.setPWM(4, 0, 0);
    pwm.setPWM(5, 0, 0);
    pwm.setPWM(6, 0, 0);
    pwm.setPWM(7, 0, 0);
    pwm.setPWM(8, 0, 0);
    pwm.setPWM(9, 0, 0);
    pwm.setPWM(10, 0, 0);
    pwm.setPWM(11, 0, 0);
  }
 }

            //Debugging outputs to terminal, remove these lines after program is completed.
 
 //Serial.println("Stalled low?");
 //Serial.println(photoStalledLow);
 
 //Serial.println("Stalled high?");
 //Serial.println(photoStalledHigh);
}

Robin2, thank you. I may borrow some of your code and see if I can make it work for my application.