Mega Decoder Sketch

updated 16/March/2021

Here is the latest version of the Mega Signal Decoder Sketch

Link to controller.h

#include "controller.h"

#include <DCC_Decoder.h>

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#include "arduino.h"

// mega signal controller Andy Moore 26/10/2020

#define OLED_RESET 0

Adafruit_SSD1306 display(OLED_RESET);
           
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2
#define DCC_Source 0
#define Sensor_Source 1


#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 
static const unsigned char PROGMEM logo16_glcd_bmp[] =  {
  0x00,0x00,0x00,0x20,0x00,0x60,0x00,0xE0,0x00,0xE0,0xEE,0xCC,0x7E,0xCE,0x7F,0xDE,
  0x7F,0xFF,0x3F,0xFB,0x3B,0x33,0x11,0x27,0x00,0x0E,0x00,0x0E,0x00,0x0C,0x00,0x00
};

static const unsigned char PROGMEM Shedend [] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
0xFF, 0xFF, 0x80, 0x78, 0x7F, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
0xFF, 0xFF, 0x00, 0x78, 0x7F, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
0xFF, 0xFE, 0x00, 0x78, 0x7F, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
0xFF, 0xFC, 0x1F, 0x78, 0x7F, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
0xFF, 0xFC, 0x3F, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
0xFF, 0xFC, 0x3F, 0xF8, 0x61, 0xFE, 0x07, 0xF0, 0x43, 0xF0, 0x3E, 0x18, 0x7F, 0x04, 0x3F, 0xFF,
0xFF, 0xFC, 0x1F, 0xF8, 0x40, 0xF8, 0x03, 0xE0, 0x03, 0xC0, 0x1E, 0x10, 0x3E, 0x00, 0x3F, 0xFF,
0xFF, 0xFE, 0x07, 0xF8, 0x00, 0x70, 0x01, 0xC0, 0x03, 0x80, 0x0E, 0x00, 0x1C, 0x00, 0x3F, 0xFF,
0xFF, 0xFE, 0x01, 0xF8, 0x30, 0x70, 0xE0, 0xC3, 0x83, 0x87, 0x06, 0x0C, 0x1C, 0x38, 0x3F, 0xFF,
0xFF, 0xFF, 0x80, 0x78, 0x78, 0x61, 0xF0, 0x87, 0xC3, 0x0F, 0x86, 0x1E, 0x18, 0x7C, 0x3F, 0xFF,
0xFF, 0xFF, 0xE0, 0x78, 0x78, 0x60, 0x00, 0x87, 0xC3, 0x00, 0x06, 0x1E, 0x18, 0x7C, 0x3F, 0xFF,
0xFF, 0xFF, 0xF8, 0x38, 0x78, 0x60, 0x00, 0x87, 0xC3, 0x00, 0x06, 0x1E, 0x18, 0x7C, 0x3F, 0xFF,
0xFF, 0xFF, 0xFC, 0x38, 0x78, 0x61, 0xFF, 0x87, 0xC3, 0x0F, 0xFE, 0x1E, 0x18, 0x7C, 0x3F, 0xFF,
0xFF, 0xFD, 0xFC, 0x38, 0x78, 0x61, 0xFF, 0x87, 0xC3, 0x0F, 0xFE, 0x1E, 0x18, 0x7C, 0x3F, 0xFF,
0xFF, 0xFC, 0xF8, 0x38, 0x78, 0x70, 0xFD, 0x83, 0x83, 0x87, 0xEE, 0x1E, 0x18, 0x38, 0x3F, 0xFF,
0xFF, 0xFC, 0x00, 0x78, 0x78, 0x70, 0x01, 0xC0, 0x03, 0x80, 0x0E, 0x1E, 0x1C, 0x00, 0x3F, 0xFF,
0xFF, 0xFC, 0x00, 0xF8, 0x78, 0x78, 0x01, 0xE0, 0x03, 0xC0, 0x0E, 0x1E, 0x1E, 0x00, 0x3F, 0xFF,
0xFF, 0xFE, 0x01, 0xF8, 0x78, 0x7E, 0x03, 0xF0, 0xC3, 0xF0, 0x1E, 0x1E, 0x1F, 0x0C, 0x3F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

//------------------------------------------------------------------------------
// File generated by LCD Assistant
// http://en.radzio.dxp.pl/bitmap_converter/
//------------------------------------------------------------------------------


static const byte major =1;
static const byte minor =2;

#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

const int SAMPLESIZE= 200;
int CurrentIndex=0;
long TimerVal=0;

String DecoderName = "Mega Decoder";
String inputString = "";
bool stringComplete = false;

unsigned long NextSemTimer = 0;


unsigned long timer = 0;
unsigned long current_time = millis();
unsigned long SerialCounter =0;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Defines and structures
//
#define kDCC_INTERRUPT            0

//
// Setup
//
void setup() 
{
  int i;
  Serial.begin(9600);
  DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);
   
  DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );  
  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
  // init done

 
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.clearDisplay();
 display.drawBitmap(0, 0,  Shedend, 128, 32,1);
//  display.display();
  display.display();
  delay(3000);
  display.clearDisplay();


  
  display.setTextSize(1);
  display.setTextColor(WHITE);

   
  display.setTextSize(1);
  display.setTextColor(WHITE);
  
  
 
 display.println(DecoderName);
 display.println("                   ");
 display.print  ("sw ver:  ");
 display.print(major); 
 display.print        (".");
 display.print(minor);
 display.println          ("            ");
 
 display.display();

  for (i=0;i<num_signals;i++)
  {
    if (signals[i].SignalType==SEMAPHORE)
    {
      Serial.print("Signal ");
      Serial.print(i);
      Serial.println(" Semaphore");
      pinMode(signals[i].RelayPin,OUTPUT);
      pinMode(signals[i].SemaphorePin,OUTPUT);
      digitalWrite(signals[i].RelayPin,LOW);
      digitalWrite(signals[i].SemaphorePin,LOW);
       
    }
    else
    {
      Serial.print("Signal ");
      Serial.print(i);
      Serial.print("  cv " );
      Serial.print(signals[i].cv);
      Serial.print(" two-aspect   ");
      Serial.print("Red pin: ");
      Serial.print(signals[i].RedPin);
      Serial.print("   Green pin: ");
      Serial.println(signals[i].GreenPin);
      pinMode(signals[i].RedPin,OUTPUT);
      pinMode(signals[i].GreenPin,OUTPUT);
    }
  }
  for (i=0;i<num_sensors;i++)
  {
    pinMode(sensors[i].Pin,INPUT_PULLUP);
  }

  for (i=0;i<num_signals;i++)
  {
    SigTimer[i].TimerActive=false;
  }
}

void HandleSensor()
{
  int i;
  for (i=0;i<num_sensors;i++)
  {
    if (digitalRead(sensors[i].Pin) == HIGH)
    {
      SensorTripped(i);
    }
    else
    {
      SensorClear(i);
    }
    DCC.loop();
  }
  
}

void CheckSemTimers()
{
  
    
      if (SigTimer[NextSemTimer].TimerActive==true)
      {
//       Serial.println("timer active ");
        if (millis() >= SigTimer[NextSemTimer].StartTime+SigTimer[NextSemTimer].TimerLength)
        {
          SigTimer[NextSemTimer].TimerActive=false;
          digitalWrite(signals[NextSemTimer].SemaphorePin, HIGH);
          Serial.print("timer expired  ");
          Serial.println(NextSemTimer);
        }
      }
    
      NextSemTimer++;
      if (NextSemTimer>=num_signals)
      {
        NextSemTimer=0;
      }
      
}

//==================================================
// 
//                  MAIN LOOP
//
//==================================================
void loop() 
{
  
 // display.clearDisplay();
 // display.setCursor(0,0);
  
  
  DCC.loop();
 
  HandleSensor();
  //DCC.loop();
  CheckSemTimers();  
 // DCC.loop();
}


void SensorTripped(int SensorNum)
{
 
    sensors[SensorNum].TripCount=0;
    if (sensors[SensorNum].Occupied==false)
    {
     Serial.print("Sensor ");
     Serial.print(SensorNum);
     Serial.println(" tripped");
     //  display.display();
       sensors[SensorNum].Occupied=true;
       Serial.print("Set signal ");
       Serial.print(sensors[SensorNum].EntrySignal);
       Serial.println(" To danger");
    }
    
    SetSignalRed(sensors[SensorNum].EntrySignal, Sensor_Source);
  
  
 
}

void SensorClear(int SensorNum)
{
  if (sensors[SensorNum].TripCount > 2)
  {
    if (sensors[SensorNum].Occupied)
    {
       sensors[SensorNum].TripCount=0;
      Serial.print("Sensor ");
      Serial.print(SensorNum);
      Serial.println(" cleared");
      //  display.display();
       sensors[SensorNum].Occupied=false;
       //if (sensors[sensors[SensorNum].PreviousSensor].Occupied==false)
       {
         if (!signals[sensors[SensorNum].ExitSignal].SignalSetToDanger)
         {
            SetSignalGreen(sensors[SensorNum].ExitSignal, Sensor_Source);
         }
       } 
    }
  }
  else
  {
    sensors[SensorNum].TripCount++;
  }
}


void TwoAspectHandler(int signum, boolean enable, int source)
{
  if (enable)
  {
    signals[signum].SignalSetToDanger = true;
    SetSignalRed(signum,source);
  }
  else
  {
    signals[signum].SignalSetToDanger = false;
    SetSignalGreen(signum,source);
  }
}


//
// Basic accessory packet handler 
//
void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data)
{
  // Convert NMRA packet address format to human address
  address -= 1;
  address *= 4;
  address += 1;
  address += (data & 0x06) >> 1;
  boolean enable = (data & 0x01) ? 1 : 0;
  // The DCC Accessory Address is now stored in "address" variable
  Serial.print(F("Basic addr: "));
  Serial.println(address, DEC);
  Serial.print(F("Activate Status: "));
  Serial.println(enable, DEC);

  for(int TempAdd = 0; TempAdd< num_signals; TempAdd++)
  {
    if(signals[TempAdd].cv == address)
    {
      Serial.print("Signal ");
      Serial.println(TempAdd,DEC);
      switch(signals[TempAdd].SignalType)
      {
         case TWO_ASPECT:
         {
            TwoAspectHandler(TempAdd,enable,DCC_Source);
            break;
         }
         case SEMAPHORE:
         {
          Serial.print("Semaphore");
          if (enable)
          {
//            if (signals[TempAdd].SemaphoreState == DANGER)
//            {
//              //do nothing
//            }
//            else
//            {
//              pulseSignal( TempAdd);
//            }
//            signals[TempAdd].SignalSetToDanger = true;
//            signals[TempAdd].SemaphoreState = DANGER;
//            digitalWrite(signals[TempAdd].RelayPin,LOW);    
                 
              SetSignalRed(TempAdd,DCC_Source);
          }
          else
          {
//            if (signals[TempAdd].SemaphoreState == DANGER)
//            {
//              pulseSignal(TempAdd);
//            }
//            signals[TempAdd].SignalSetToDanger = false;
//            signals[TempAdd].SemaphoreState = CLEAR;
//            digitalWrite(signals[TempAdd].RelayPin,HIGH);      
              SetSignalGreen(TempAdd,DCC_Source);  
          }  
          break;
         }
         case SEMAPHORE_TOGGLE:
         {
           pulseSignal( TempAdd);
           break;
         }
      }//switch
      TempAdd=num_signals; //get out of here
    }// valid cv
  }//for loop
}

void pulseSignal(int sigNum)
{
            //Serial.println("pulse signal");
            digitalWrite(signals[sigNum].SemaphorePin,LOW);
            
            //digitalWrite( pinNum,LOW);
            
            StartTimer(sigNum,400);
            //delay(150);
            //digitalWrite( pinNum,HIGH);
           
            
}

void StartTimer(int Signum, unsigned long TimeToWait)
{
   Serial.println ("Start Timer");
   
   SigTimer[Signum].StartTime = millis();
   SigTimer[Signum].TimerLength = TimeToWait;
   SigTimer[Signum].TimerActive = true;

   Serial.print ("signal number ");
   Serial.println (Signum);
   Serial.print ("Start Time ");
   Serial.println (SigTimer[Signum].StartTime);
   Serial.print ("time to wait ");
   Serial.println (SigTimer[Signum].TimerLength);
}

void SetSignalRed(int Signum, int source)
{
        
        
        if (signals[Signum].SignalType==SEMAPHORE)
        {
          if (source==DCC_Source)
          {
            Serial.print(Signum, DEC);
            Serial.println(" To danger");
            signals[Signum].DCC_State = DANGER;
          }
          else
          {
            signals[Signum].Sensor_State = DANGER;
          }
          if (signals[Signum].SemaphoreState != DANGER)
          {
              Serial.print(Signum, DEC);
              Serial.println(" To danger");
              pulseSignal( Signum);
              signals[Signum].SemaphoreState = DANGER;
              digitalWrite(signals[Signum].RelayPin,LOW);
          }

          
//          if (signals[Signum].SignalSetToDanger)
//          {
//            //do nothing
//          }
//          else
//          {
//            if (signals[Signum].SemaphoreState == CLEAR)
//            {
//              //Serial.print("Pulse signal ");
//              Serial.print(Signum, DEC);
//              Serial.println(" To danger");
//              pulseSignal( Signum);
//              signals[Signum].SemaphoreState = DANGER;
//              digitalWrite(signals[Signum].RelayPin,LOW);
//            }           
//          }
        }
        else
        {
          
          digitalWrite(signals[Signum].RedPin,LOW);
          digitalWrite(signals[Signum].GreenPin,HIGH);
          
        }
}

void SetSignalGreen(int Signum, int source)
{
  if (signals[Signum].SignalType==SEMAPHORE)
  {

     if (source == DCC_Source)
     {
        Serial.print(Signum, DEC);
        Serial.println(" To CLEAR");
        signals[Signum].DCC_State = CLEAR;
     }
     else
     {
        signals[Signum].Sensor_State = CLEAR;
     }
     
     if (signals[Signum].SemaphoreState != CLEAR)
     {
        if ((signals[Signum].DCC_State == CLEAR) && (signals[Signum].Sensor_State == CLEAR))
        {
          //    Serial.print("Pulse signal ");
              Serial.print(Signum, DEC);
              Serial.println(" To clear");
              pulseSignal( Signum);
              signals[Signum].SemaphoreState = CLEAR;
              digitalWrite(signals[Signum].RelayPin,HIGH);
        }
          
        
     }
  }
  else
  {
         Serial.print(F("SetSignalGreen: "));
         Serial.println(Signum, DEC);
         //analogWrite(signals[Signum].RelayPin, 255);
          digitalWrite(signals[Signum].RedPin,HIGH);
          digitalWrite(signals[Signum].GreenPin,LOW);
  }
}