Adding a fade to working model railroad signaling project

I have this project that has been working well for a few months now. It utilizes an Arduino Uno talking to Java Model Railroad Interface (JMRI). The Uno drives 5 - PCA9685 Servo Boards to drive model railroad signals on the train layout.

I would like to add in a fade to the LEDs so they appear as an incandescent bulb.

I have tried to add some "for loops" and messed with "delay" but ultimately caused the signals to stop working, or just delayed how fast they changed indications.

Any advice how to add this feature?

This project only has to run signals. So my thought is a delay function shouldn't harm any other actions.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <CMRI.h>
#include <Auto485.h>

#define CMRI_ADDR 1
#define DE_PIN 2
#define numServos 80 //The number of servos connected

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40); //setup the board address 0
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41); //setup the board address 0
Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(0x42); //setup the board address 0
Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(0x43); //setup the board address 0
Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(0x44); //setup the board address 0

Auto485 bus(DE_PIN); // Arduino pin 2 -> MAX485 DE and RE pins
CMRI cmri(CMRI_ADDR, 16, 88, bus);

int Status[numServos]; //Create a table to hold the status of each turnout, signal, etc.
int Throw[numServos]; //Create a table to hold the throw value for each servo
int Close[numServos]; //Create a table to hold the close value for each servo

void setup() {
  Serial.begin(9600);
  bus.begin(9600);
  pwm1.begin();
  pwm1.setPWMFreq(50);  // This is the maximum PWM frequency
  pwm2.begin();
  pwm2.setPWMFreq(50);  // This is the maximum PWM frequency
  pwm3.begin();
  pwm3.setPWMFreq(50);  // This is the maximum PWM frequency
  pwm4.begin();
  pwm4.setPWMFreq(50);  // This is the maximum PWM frequency
  pwm5.begin();
  pwm5.setPWMFreq(50);  // This is the maximum PWM frequency
 

  //SET THE BRIGHTNESS VALUE OF LEDS


//PIN connection 1 - SPARE - NOT IN USE
//Throw[0] = 0; //NOT IN USE
//Close[0] = 0; //NOT IN USE

// BEGIN SIGNAL LOCATION:CP: SOUTH EDISTO ABSOLUTE SIGNALS
//         vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

  //PIN connection 2 - NO SIGNAL YET
  Throw[1] = 0;
  Close[1] = 0;  

  //PIN connection 3 - NO SIGNAL YET
  Throw[2] = 0;
  Close[2] = 0;

  //PIN connection 4 - NO SIGNAL YET
  Throw[3] = 0;
  Close[3] = 0;
  
  //PIN connection 5 - NO SIGNAL YET
  Throw[4] = 0;
  Close[4] = 0;
  
  //PIN connection 6 - NO SIGNAL YET
  Throw[5] = 0;
  Close[5] = 0;
  
  //PIN connection 7 - NO SIGNAL YET
  Throw[6] = 0;
  Close[6] = 0;

  //PIN connection 8 - NO SIGNAL YET
  Throw[7] = 0;
  Close[7] = 0;

  //PIN connection 9 - NO SIGNAL YET
  Throw[8] = 0;
  Close[8] = 0;

  //PIN connection 10 - NO SIGNAL YET
  Throw[9] = 0;
  Close[9] = 0;

  //PIN connection 11 - NO SIGNAL YET
  Throw[10] = 0;
  Close[10] = 0;
  
  //PIN connection 12 - NO SIGNAL YET
  Throw[11] = 0;
  Close[11] = 0;
  
  //PIN connection 13 - NO SIGNAL YET
  Throw[12] = 0;
  Close[12] = 0;
  
  //PIN connection 14
  Throw[13] = 1000;
  Close[13] = 0;

  //PIN connection 15
  Throw[14] = 1000;
  Close[14] = 0;

  //PIN connection 16 - SPARE - NOT IN USE
  Throw[15] = 0; //NOT IN USE
  Close[15] = 0; //NOT IN USE

    //PIN connection 17 - SPARE - NOT IN USE
  Throw[16] = 0; //NOT IN USE
  Close[16] = 0; //NOT IN USE

  //PIN connection 18
  Throw[17] = 1000;
  Close[17] = 0;

  //PIN connection 19
  Throw[18] = 1000;
  Close[18] = 0;

  //PIN connection 20
  Throw[19] = 1000;
  Close[19] = 0;

  //PIN connection 21
  Throw[20] = 1000;
  Close[20] = 0;
  
  //PIN connection 22 -
  Throw[21] = 1000;
  Close[21] = 0;
  
  //PIN connection 23 -
  Throw[22] = 1000;
  Close[22] = 0;
  
  //PIN connection 24 -
  Throw[23] = 1000;
  Close[23] = 0;

  //PIN connection 25 -
  Throw[24] = 1000;
  Close[24] = 0;

  //PIN connection 26 -
  Throw[25] = 1000;
  Close[25] = 0;

  //PIN connection 27 -
  Throw[26] = 1000;
  Close[26] = 0;

  //        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// END SIGNAL LOCATION:CP: SOUTH EDISTO ABSOLUTE SIGNALS
//
// BEGIN SIGNAL LOCATION: STONO INTERMEDIATE SIGNALS
//         vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 

  //PIN connection 28
  Throw[27] = 1000;
  Close[27] = 000;
 
  //PIN connection 29
  Throw[28] = 1000;
  Close[28] = 000;
  
  //PIN connection 30
  Throw[29] = 1000;
  Close[29] = 000;
  
  //PIN connection 31
  Throw[30] = 1000;
  Close[30] = 000;

  //PIN connection 32 - SPARE - NOT IN USE
  Throw[31] = 0; //NOT IN USE
  Close[31] = 0; //NOT IN USE

   //PIN connection 33 - SPARE - NOT IN USE
  Throw[32] = 0; //NOT IN USE
  Close[32] = 0; //NOT IN USE

 //PIN connection 34
  Throw[33] = 1000;
  Close[33] = 000;

  //PIN connection 35
  Throw[34] = 1000;
  Close[34] = 000;

  //PIN connection 36 - GREEN
  Throw[35] = 1000;
  Close[35] = 000;

  //PIN connection 37 - YELLOW
  Throw[36] = 1000;
  Close[36] = 000;
  
  //PIN connection 38 - RED
  Throw[37] = 1000;
  Close[37] = 000;
  
  //PIN connection 39 - GREEN
  Throw[38] = 1000;
  Close[38] = 000;
  
  //PIN connection 40 - YELLOW
  Throw[39] = 1000;
  Close[39] = 000;

  //PIN connection 41 - RED
  Throw[40] = 1000;
  Close[40] = 000;

  //PIN connection 42 - GREEN
  Throw[41] = 1000;
  Close[41] = 000;

 //PIN connection 43 - YELLOW
  Throw[42] = 1000;
  Close[42] = 000;

  //PIN connection 44 - RED
  Throw[43] = 1000;
  Close[43] = 000;

  //PIN connection 45 - GREEN
  Throw[44] = 1000;
  Close[44] = 000;
  
  //PIN connection 46 - YELLOW
  Throw[45] = 1000;
  Close[45] = 000;
  
  //PIN connection 47 - RED
  Throw[46] = 1000;
  Close[46] = 000;

  //PIN connection 48 - GREEN
  Throw[47] = 1000; 
  Close[47] = 000; 

  //PIN connection 50 - DAMAGED
  Throw[48] = 000; //NOT IN USE
  Close[48] = 000; //NOT IN USE

  //PIN connection 50 - DAMAGED
  Throw[49] = 000; //NOT IN USE
  Close[49] = 000; //NOT IN USE

  //PIN connection 51 - YELLOW 
  Throw[50] = 1000;
  Close[50] = 000;

  //PIN connection 52 - RED
  Throw[51] = 1000;
  Close[51] = 000;
  
  //PIN connection 53 - GREEN
  Throw[52] = 1000;
  Close[52] = 000;
  
  //PIN connection 54 - YELLOW
  Throw[53] = 1000;
  Close[53] = 000;

  //PIN connection 55 - RED
  Throw[54] = 1000;
  Close[54] = 000;

//        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// END SIGNAL LOCATION: CP: STONO INTERMEDIATE SIGNALS
//
// BEGIN SIGNAL LOCATION: CYPRESS INTERMEDIATE SIGNALS
//         vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 

  //PIN connection 56 - 
  Throw[55] = 1000;
  Close[55] = 000;

 //PIN connection 57 - YELLOW
  Throw[56] = 1000;
  Close[56] = 000;

  //PIN connection 58 - RED
  Throw[57] = 1000;
  Close[57] = 000;

  //PIN connection 59 - YELLOW
  Throw[58] = 1000;
  Close[58] = 000;

  //PIN connection 60 - RED
  Throw[59] = 1000;
  Close[59] = 000;

   //PIN connection 61 - GREEN
  Throw[60] = 1000;
  Close[60] = 000;

 //PIN connection 62 - 
  Throw[61] = 1000;
  Close[61] = 000;

  //PIN connection 63 - 
  Throw[62] = 1000;
  Close[62] = 000;

  //PIN connection 64 - 
  Throw[63] = 1000;
  Close[63] = 000;

  //PIN connection 65 - 
  Throw[64] = 1000;
  Close[64] = 000;

  //PIN connection 66 - GREEN
  Throw[65] = 1000;
  Close[65] = 000;

  //PIN connection 67 - YELLOW
  Throw[66] = 1000;
  Close[66] = 000;

  //PIN connection 68 - 
  Throw[67] = 1000;
  Close[67] = 000;

  //PIN connection 69 - 
  Throw[68] = 1000;
  Close[68] = 000;

  //PIN connection 70 - 
  Throw[69] = 1000;
  Close[69] = 000;

  //PIN connection 71 - 
  Throw[70] = 1000;
  Close[70] = 00;

  //PIN connection 72 - 
  Throw[71] = 1000;
  Close[71] = 000;

  //PIN connection 73 - 
  Throw[72] = 1000;
  Close[72] = 000;

  //PIN connection 74 - 
  Throw[73] = 1000;
  Close[73] = 000;

  //PIN connection 75 - GREEN
  Throw[74] = 1000;
  Close[74] = 000;

  //PIN connection 76 - 
  Throw[75] = 1000;
  Close[75] = 000;

 //PIN connection 77 - 
  Throw[76] = 1000;
  Close[76] = 000;

  //PIN connection 78 - 
  Throw[77] = 1000;
  Close[77] = 000;

  //PIN connection 79 - 
  Throw[78] = 1000;
  Close[78] = 000;

 //PIN connection 80 - 
  Throw[79] = 1000;
  Close[79] = 000;

  //PIN connection 0 - 
  Throw[80] = 000;
  Close[80] = 000;
}

void loop(){
   cmri.process();

    for (int i = 0; i < 16; i++) {
        
        //BOARD 1
        Status[i] = (cmri.get_bit(i));
        if (Status[i] == 1){
            pwm1.writeMicroseconds(i, Throw[i]);
        }
        else {
            pwm1.writeMicroseconds(i, Close[i]);
        }

           //BOARD 2
        Status[i+16] = (cmri.get_bit(i+16));
        if (Status[i+16] == 1){
            pwm2.writeMicroseconds(i, Throw[i+16]);
        }
        else {
            pwm2.writeMicroseconds(i, Close[i+16]);
        }

           //BOARD 3
        Status[i+32] = (cmri.get_bit(i+32));
        if (Status[i+32] == 1){
            pwm3.writeMicroseconds(i, Throw[i+32]);
        }
        else {
            pwm3.writeMicroseconds(i, Close[i+32]);
        }

        //BOARD 4
        Status[i+48] = (cmri.get_bit(i+48));
        if (Status[i+48] == 1){
            pwm4.writeMicroseconds(i, Throw[i+48]);
        }
        else {
            pwm4.writeMicroseconds(i, Close[i+48]);
        }
        //BOARD 5
        Status[i+64] = (cmri.get_bit(i+64));
        if (Status[i+64] == 1){
            pwm5.writeMicroseconds(i, Throw[i+64]);
        }
        else {
            pwm5.writeMicroseconds(i, Close[i+64]);
        }
           
       
    }
}

I think you'd need another array 80 long to hold fade levels, and complicate the math to do the fade accounting:

        ...
        Status[i] = (cmri.get_bit(i));
        if (Status[i] == 1){
            if( fade[i] < Throw[i]) fade[i]++;
            pwm1.writeMicroseconds(i, fade[i]);
        }
        else { 
            if (fade[i] > 0) fade[i]--;
            pwm1.writeMicroseconds(i, Close[i]+fade[i]);
        }
        ...

redundant code can be reduced and made easier to maintain

convention is to only Capitalize constants, however, "throw" is a keyword.

added setting [] to store intermediate "fade" values between Throw/Close

consider

#define MyHW
#ifdef MyHW
struct Adafruit_PWMServoDriver  {
    Adafruit_PWMServoDriver ( int i)  { }
    void begin      (void)  { }
    void setPWMFreq (int i)  { }
    void writeMicroseconds (int i, int j)  { }
};

struct Auto485  {
    Auto485 (int i)   { }
    void begin (int i) { }
};

struct CMRI  {
    CMRI  (int adr, int i, int j, Auto485 b) { }
    int  get_bit (int i)   { return 0; }
    void process (void)    { }
};

#else
# include <Adafruit_PWMServoDriver.h>
# include <CMRI.h>
# include <Auto485.h>
#endif

#include <Wire.h>

#define CMRI_ADDR 1
#define DE_PIN 2
#define numServos 80 //The number of servos connected
Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40); //setup the board address 0
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41); //setup the board address 0
Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(0x42); //setup the board address 0
Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(0x43); //setup the board address 0
Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(0x44); //setup the board address 0

Auto485 bus(DE_PIN); // Arduino pin 2 -> MAX485 DE and RE pins
CMRI cmri(CMRI_ADDR, 16, 88, bus);

// statically define and initialized arrays
#define N  1000
int close   [numServos];    // initialized to zero
int setting [numServos];    // initialized to zero

int Throw   [numServos] = { // throw is keyword and cannot be used
    0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, N, N, 0,
    0, N, N, N,  N, N, N, N,  N, N, N, N,  N, N, N, 0,
    0, N, N, N,  N, N, N, N,  N, N, N, N,  N, N, N, N,
    0, 0, N, N,  N, N, N, N,  N, N, N, N,  N, N, N, N,
    N, N, N, N,  N, N, N, N,  N, N, N, N,  N, N, N, N,
};


// -----------------------------------------------------------------------------
void setup() {
    Serial.begin(9600);
    bus.begin(9600);
    pwm1.begin();
    pwm1.setPWMFreq(50);  // This is the maximum PWM frequency
    pwm2.begin();
    pwm2.setPWMFreq(50);  // This is the maximum PWM frequency
    pwm3.begin();
    pwm3.setPWMFreq(50);  // This is the maximum PWM frequency
    pwm4.begin();
    pwm4.setPWMFreq(50);  // This is the maximum PWM frequency
    pwm5.begin();
    pwm5.setPWMFreq(50);  // This is the maximum PWM frequency

}

// -----------------------------------------------------------------------------
void update (
    Adafruit_PWMServoDriver *pwm,
    int                      offset )
{
    for (int i = 0; i < 16; i++) {
        int n = offset + i;

        if (1 ==  cmri.get_bit (n))  {
            if (setting [n] < Throw [n])
                setting [n] += 100;
        }
        else  {
            if (setting [n] > close [n])
                setting [n] -= 100;
        }

        pwm->writeMicroseconds (i, setting [n]);
    }
}

// -----------------------------------------------------------------------------
void loop(){
    cmri.process();

    update (& pwm1, 0);
    update (& pwm2, 16);
    update (& pwm3, 32);
    update (& pwm4, 48);
    update (& pwm5, 64);
}

I was thinking even the objects can be declared as an Array

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <CMRI.h>
#include <Auto485.h>

#define CMRI_ADDR 1
#define DE_PIN 2
#define numServos 80 //The number of servos connected

Adafruit_PWMServoDriver pwmServ[5] = {Adafruit_PWMServoDriver(0x40), Adafruit_PWMServoDriver(0x41),
                                      Adafruit_PWMServoDriver(0x42), Adafruit_PWMServoDriver(0x43),
                                      Adafruit_PWMServoDriver(0x44)
                                     } ;
Auto485 bus(DE_PIN); // Arduino pin 2 -> MAX485 DE and RE pins
CMRI cmri(CMRI_ADDR, 16, 88, bus);

int Status[numServos]; //Create a table to hold the status of each turnout, signal, etc.
int Throw[numServos]; //Create a table to hold the throw value for each servo
int Close[numServos]; //Create a table to hold the close value for each servo

void setup() {
  Serial.begin(9600);
  bus.begin(9600);
  for (uint8_t i = 0; i < 5; i++) {
    pwmServ[i].begin();
    pwmServ[i].setPWMFreq(50);  // This is the maximum PWM frequency
  }

  for (uint8_t i = 0; i < 80; i++) {
    if ((i > 16) && (i != 31) && (i != 32) && (i != 48) && (i != 49)) {
      Throw[i] = 1000;
    }
    else Throw[i] = 0;
    Close[i] = 0;
  }
}

void loop() {
  cmri.process();
  for (uint8_t i = 0; i < 16; i++) {
    for (uint8_t j = 0; j < 5; j++) {
      Status[i] = (cmri.get_bit(i));
      if (Status[i] == 1) {
        pwmServ[j].writeMicroseconds(i, Throw[i + j * 16]);
      }
      else {
        pwmServ[j].writeMicroseconds(i, Close[i + j * 16]);
      }
    }
  }
}

Don't know if it works though really, but it sure makes for readable code.

Depends, 'throw' is a keyword of what ? keywords that are used by libraries you used, may not be used, though even then, there is nothing stopping us from creating a function called 'setPixelColor()' even if we are using adafruit_neopixel.h, and similar for many of the other keywords (that light up the IDE)

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.