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);
}
}