Project showcase
RAGLINK+ CabViewer

RAGLINK+ CabViewer © GPL3+

A desktop train controller based on Arduino for OpenBVE.

  • 2,101 views
  • 0 comments
  • 12 respects

Components and supplies

Ardgen mega
Arduino Mega 2560 & Genuino Mega 2560
With Sensor Sheild.
×1
  7
Itead Nextion NX8048T070 - Generic 7.0" HMI TFT LCD Touch Display
HMI firmware provided.
×2
KHT-2 16W1D Band Switch
×1
Adafruit industries ada260 image 90px
USB-A to Mini-USB Cable
×1
CH340G USB-TTL Cable
×1
PC computer with triple screen supported GPU (generic)
×1
Computer LCD Monitor (generic)
×3
LA38-11 LA38-11BN 22mm button
×8
LA38-11X2 20X3 22mm switch
×1
AD16-16C 16mm LED
×2
LAY37-11Y2 20Y3 22mm key switch
×1
Acrylic sheet 5mm (generic)
Devices laser cutting engineering files provided.
×4

Necessary tools and machines

Lasercutter
Laser cutter (generic)
3drag
3D Printer (generic)
09507 01
Soldering iron (generic)
Hy gluegun
Hot glue gun (generic)

Apps and online services

About this project

A desktop train controller based on Arduino for OpenBVE.

Controller

  • Board: Arduino MEGA 2560 / Arduino DUE
  • RAGLINK+ CabViewer Controller Version: 3.1.1028
  • RAGLINK+ CabViewer OpenBVE Proxy Version: 3.1.1021
  • OpenBVE Version: 1.4.1135.867
  • Lincense: GPL

Demo Images

Code

Source Code for Arduino MEGA2560C/C++
/*Desktop Train Controller
===========================================
--Board: Arduino MEGA 2560
--Version: 1.0
--Simulator: OpenBVE
===========================================
--Note:
State Automatic Ready
===========================================
--Devices:
Type:0.SWITCH_C -> CHANGE
Type:1.SWITCH_F -> FALLING
Type:3.ENCODER -> CHANGE (in developing)
===========================================
--Train:
0.SPEED -> INT
1.REVERSER -> INT
2.POWER -> U8
3.BRAKE -> U8
4.SIGNAL -> INT
5.SIGNAL_DISTANCE -> INT
6.SPEED_LIMIT -> INT
7.HORN -> BOOL
8.SPEED_CONST -> INT
9.MASTER_KEY -> BOOL
===========================================
*/
#define SWITCH_C 0
#define SWITCH_F 1
#define ENCODER 2
#define DIG_OUT 3
#define ANALOG_OUT 4
#define DELAY_TIME 10
#define IGNORE -1
#define COUNT 2
#define KEY_UP 0
#define KEY_WAITTING 1
#define KEY_DOWN 2
#define SA_COUNT_SF 500
#define SA_COUNT_SC 300
//
#define SPEED_MIN 0
#define SPEED_MAX 400
#define REVERSER_FORWARD 1
#define REVERSER_NEUTRAL 0
#define REVERSER_BACKWARD -1
#define POWER_MIN 0
#define POWER_MAX 4
#define BRAKE_MIN 0
#define BRAKE_MAX 8
#define SIGNAL_RED 0
#define SIGNAL_YELLOW 1
#define SIGNAM_GREEN 2
#define SIGNAL_DISTANCE_N1 2000
#define SIGNAL_DISTANCE_N2 1500
#define SIGNAL_DISTANCE_N3 1000
#define SIGNAL_DISTANCE_N4 500
#define SIGNAL_DISTANCE_DE 0
#define SIGNAL_PASS 0
#define HORN_OFF 0
#define HORN_ON 1
#define SPEED_LIMIT_MIN 0
#define SPEED_LIMIT_DEF 30
#define SPEED_LIMIT_MAX 400
#define SPEED_CONST_MIN 0
#define SPEED_CONST_DEF 30
#define SPEED_CONST_MAX 400
#define MASTER_KEY_OFF 0
#define MASTER_KEY_ON 1
#define EMERGENCY_ON 1
#define EMERGENCY_OFF 0
#define OVERWRITE 0
#define NORMAL 1
//
#define LDOOR_OPEN_DEF 0
#define RDDOR_OPEN_DEF 0
#define SANDER_OFF 0
#define PANTO_UP 1
#define LIGHT_OFF 0
#define CURRENT_STATION_N 0
#define NEXT_STATION_N 0
#define NEXT_STATION_DIS_DEF 0
#define CURRENT_STATION_DEPART_TIME_DEF 0
#define NEXT_STATION_ARRIVAL_TIME_DEF 0
#define CURRENT_TIME_DEF 0
#define LDOOR_IN_OP_DEF 0
#define RDOOR_IN_OP_DEF 0
//Devices
#define DEVICE_NUMBER 17
#define DEVICE_TYPE_NUMBER 3
#define ACTIVE 0
#define NO_ACTIVE 1
#define NO_READY 0
#define READY 1
#define ON 1
#define OFF 0
#define ANALOG_OUT_MIN 0
#define ANALOG_OUT_MAX 255
//Train
#define TRAIN_DATA_NUMBER 25
#define _INT 0
#define _BOOL 1
#define _STRING 3
#define SPEED 0
#define REVERSER 1
#define POWER 2
#define BRAKE 3
#define SIGNAL_INFO 4
#define SIGNAL_DISTANCE 5
#define SPEED_LIMIT 6
#define HORN 7
#define SPEED_CONST 8
#define MASTER_KEY 9
#define EMERGENCY 10
#define RC_MODE 11
#define LDOOR_OPEN 12
#define RDOOR_OPEN 13
#define LIGHT_OPEN 15
#define PANTO_OPEN 16
#define SANDER_OPEN 14
#define CURRENT_STATION_NAME 17
#define NEXT_STATION_NAME 18
#define CURRENT_STATION_DEPART 20
#define NEXT_STATION_ARRIVAL 21
#define NEXT_STATION_DIS 19
#define CURRENT_TIME 22
#define LDDOR_IN_OP 23
#define RDOOR_IN_OP 24
#define UPDATE_LAST_NUM 14
#define UPDATE_OW 8
//PC
#define FILTER '|'
#define START_SYM '#'
#define END_SYM '!'
#define NO_DATA ""
#define RECIEVE_DELAY 2
#define SEND_DELAY 25
#define TIMER_TICK 200
//Special Args
#define CONTROLLER_INFO "#ver|"

#define NOP do { __asm__ __volatile__ ("nop"); } while (0)

#include "FlexiTimer2.h"
#include "avr/wdt.h"
#include "HardwareSerial.h"

typedef void (*funcPoint)();
//
/*
	===========================================
	--Device Maps
		default:
		PowerUp:process0
		PowerDown:process1
		ReserverForward:process2
		ReserverBackward:process3
		Horn:process4
		SpeedConst:process5
		Emergency:process6
		MasterKey:process7
	===========================================
*/

void process0();
void process1();
void process2();
void process3();
void process4();
void process5();
void process6();
void process7();
void process8();
void process9();
void process10();
void process11();
void process12();
void process13();
void process14();
void process15();
void process16();

//change device type here
const int deviceType[DEVICE_NUMBER] = {SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C,
                                       SWITCH_C, DIG_OUT, SWITCH_C, DIG_OUT, SWITCH_C, SWITCH_C, SWITCH_C
                                      };
//change functions here
const int devicePinsType[] = {INPUT_PULLUP, INPUT_PULLUP, INPUT_PULLUP, OUTPUT};
const funcPoint Process[DEVICE_NUMBER] = {process0, process1, process2, process3, process4, process5, process6, process7, process8, process9,
                                          process10, process11, process12, process13, process14, process15, process16
                                         };
//all use PULL_UP gpio mode
//key for bac horn -3 -2 -1 1 2 3 ld ldled rd rdled light pat sand
const int devicePins[DEVICE_NUMBER] = {31, 32, 33, 37, 25, 26, 27, 28, 29, 30, 39, 41, 22, 24, 35, 34, 38};
int deviceLastState[DEVICE_NUMBER] = {KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP,
                                      KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP
                                     };
int deviceSADur[DEVICE_NUMBER] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                  0, 0, 0, 0, 0, 0, 0
                                 };
//train data id
const int dataDefault[TRAIN_DATA_NUMBER] = {SPEED_MIN, REVERSER_NEUTRAL, POWER_MIN, BRAKE_MIN, SIGNAL_RED,
                                            SIGNAL_DISTANCE_DE, SPEED_LIMIT_DEF, HORN_OFF, SPEED_CONST_MIN, MASTER_KEY_OFF,
                                            EMERGENCY_OFF, NORMAL, LDOOR_OPEN_DEF, RDDOR_OPEN_DEF, SANDER_OFF, LIGHT_OFF, PANTO_UP, CURRENT_STATION_N, NEXT_STATION_N,
                                            NEXT_STATION_DIS_DEF, CURRENT_STATION_DEPART_TIME_DEF, NEXT_STATION_ARRIVAL_TIME_DEF, CURRENT_TIME_DEF, LDOOR_IN_OP_DEF, RDOOR_IN_OP_DEF
                                           };
const int dataType[TRAIN_DATA_NUMBER] = {_INT, _INT, _INT, _INT, _INT, _INT, _INT, _BOOL, _INT, _BOOL, _BOOL, _BOOL, _BOOL, _BOOL,
                                         _BOOL, _BOOL, _INT, _STRING, _STRING, _STRING, _STRING, _STRING, _STRING, _BOOL, _BOOL
                                        };
const int recieveToUpdate[UPDATE_LAST_NUM] = {SPEED, SIGNAL_INFO, SIGNAL_DISTANCE, SPEED_LIMIT, RC_MODE, LDDOR_IN_OP, RDOOR_IN_OP, NEXT_STATION_NAME, CURRENT_STATION_NAME,
                                              NEXT_STATION_DIS, NEXT_STATION_ARRIVAL, CURRENT_STATION_DEPART, NEXT_STATION_DIS, CURRENT_TIME
                                             };
const int overwriteToUpdate[UPDATE_OW] = {POWER, BRAKE, REVERSER, SPEED, SIGNAL_INFO, SIGNAL_DISTANCE, SPEED_LIMIT, RC_MODE};

class TrainManager
{
public:
	int trainData[TRAIN_DATA_NUMBER];
	String trainDataStr[TRAIN_DATA_NUMBER];
	//did it has been sended
	TrainManager(const int dataDefault[])
	{
		//set default data to Train Manager
		for (int i = 0; i < TRAIN_DATA_NUMBER; i++)
		{
			trainData[i] = dataDefault[i];
			trainDataStr[i] = NO_DATA;
		}
	}
	//
	void SetData(int dataID, int value)
	{
		trainData[dataID] = value;
	}
	void SetDataStr(int dataID, String value)
	{
		trainDataStr[dataID] = value;
	}
	//get train data
	int GetData(int dataID)
	{
		return trainData[dataID];
	}
	String GetDataStr(int dataID)
	{
		return trainDataStr[dataID];
	}
};

class DevicesManager
{
public:
	DevicesManager(const int devicePins[], const int deviceType[], const int devicePinsType[])
	{
		//define gpio mode
		for (int i = 0; i < DEVICE_NUMBER; i++)
			pinMode(devicePins[i], devicePinsType[deviceType[i]]);
	}
	//
	int GetState(int deviceID)
	{
		if (deviceType[deviceID] == SWITCH_F)
		{
			if (digitalRead(devicePins[deviceID]) == LOW)
			{
				if (deviceLastState[deviceID] == KEY_UP)
				{
					deviceSADur[deviceID] = 0;
					deviceLastState[deviceID] = KEY_WAITTING;
				}
				else if (deviceLastState[deviceID] == KEY_WAITTING)
				{
					deviceSADur[deviceID]++;
					if (deviceSADur[deviceID] >= SA_COUNT_SF)
					{
						deviceSADur[deviceID] = 0;
						deviceLastState[deviceID] = KEY_DOWN;
						return ACTIVE;
					}
				}
			}
			else
			{
				deviceLastState[deviceID] = KEY_UP;
				return NO_ACTIVE;
			}
		}
		else if (deviceType[deviceID] == SWITCH_C)
		{
			if (digitalRead(devicePins[deviceID]) == LOW)
			{
				if (deviceLastState[deviceID] == KEY_UP)
				{
					deviceSADur[deviceID] = 0;
					deviceLastState[deviceID] = KEY_WAITTING;
				}
				else if (deviceLastState[deviceID] == KEY_WAITTING)
				{
					deviceSADur[deviceID]++;
					if (deviceSADur[deviceID] >= SA_COUNT_SC)
					{
						deviceSADur[deviceID] = 0;
						deviceLastState[deviceID] = KEY_DOWN;
						return ACTIVE;
					}
				}
				else if (deviceLastState[deviceID] == KEY_DOWN)return ACTIVE;
			}
			else
			{
				deviceLastState[deviceID] = KEY_UP;
				return NO_ACTIVE;
			}
		}
		return NO_ACTIVE;
	}
	//
	void delay_(int ms)
	{
		for (int i = 0; i < ms; i++)
		{
			for (int j = 0; j < 1985; j++) NOP;
		}
	}
};

DevicesManager Devices(devicePins, deviceType, devicePinsType);
TrainManager currentData(dataDefault);
TrainManager processData(dataDefault);

class CommunicationManager
{
private:
	String recieveData;
	String sendData;
	String sender;
	String tmp;
	int length, st, ed, pos, sendEd;
public:
	CommunicationManager()
	{
		Serial.begin(115200);
		//reset the recieved data
		sendData = NO_DATA;
		recieveData = NO_DATA;
		sender = NO_DATA;
		length = st = ed = pos = 0;
		sendEd = 0;
	}

	bool IsSpecialArgs(String recieveData)
	{
		if (recieveData == CONTROLLER_INFO)return true;
		return false;
	}

	bool SendControllerInfo()
	{
		String sendData = NO_DATA;
		sendData += START_SYM;
		sendData += "ver|3.1";
		sendData += END_SYM;
		Serial.print(sendData);
	}

	bool RecieveDataFromPC(TrainManager & p)
	{
		tmp = NO_DATA;
		//clear last data
		recieveData = NO_DATA;
		//recieve from serial
		while (Serial.available() > 0)
		{
			char currentRead = char(Serial.read());
			recieveData += currentRead;
			Devices.delay_(RECIEVE_DELAY);
			//if (currentRead == END_SYM)break;
		}
		length = recieveData.length();
		//no data exit
		if (!length)return false;
		//find start pos
		//Serial.println(recieveData);

		//Special Args
		if (IsSpecialArgs(recieveData))
		{
			SendControllerInfo();
			return true;
		}

		st = ed = 0;
		for (int i = 0; i < length; i++)
			if (recieveData.charAt(i) == START_SYM)
			{
				st = i;
				break;
			}
		//find end pos
		for (int i = length - 1; i >= 0; i--)
			if (recieveData.charAt(i) == END_SYM)
			{
				ed = i;
				break;
			}
		//cover data
		pos = 0;
		for (int i = st; i <= ed; i++)
		{
			if (recieveData.charAt(i) != START_SYM && recieveData.charAt(i) != END_SYM && recieveData.charAt(i) != FILTER)tmp += recieveData.charAt(i);
			if (recieveData.charAt(i) == FILTER)
			{
				//send data to TrainManager
				if (dataType[pos] != _STRING)
					p.SetData(pos++, tmp.toInt());
				else
				{
					p.SetDataStr(pos++, tmp);
				}
				//clear tmp
				tmp = NO_DATA;
			}
		}
		return true;
	}
	//
	bool SendDataToPC(TrainManager & p)
	{
		sendData = NO_DATA;
		//add start symbol
		sendData += START_SYM;
		//add contents
		for (int i = 0; i < TRAIN_DATA_NUMBER; i++)
		{
			if (dataType[i] != _STRING)sendData += p.GetData(i);
			else sendData += " ";
			sendData += FILTER;
		}
		//add end symbol
		sendData += END_SYM;
		if (!sendData.length())return false;
		//send data to PC
		Serial.print(sendData);
		return true;
	}
};

class TaskManager
{
public:
	TaskManager()
	{
		currentData = processData;
	}
	//
	void AddProc(TrainManager & p)
	{
		currentData = p;
	}
	//
	void GetLastState(TrainManager & p)
	{
		p = currentData;
	}
	//
	void UpdateData(TrainManager & p)
	{
		if (currentData.GetData(RC_MODE) == NORMAL)
			for (int i = 0; i < UPDATE_LAST_NUM; i++)
			{
				if (dataType[recieveToUpdate[i]] != _STRING)
					p.SetData(recieveToUpdate[i], currentData.GetData(recieveToUpdate[i]));
				else
					p.SetDataStr(recieveToUpdate[i], currentData.GetDataStr(recieveToUpdate[i]));
			}
		else if (currentData.GetData(RC_MODE) == OVERWRITE)
			for (int i = 0; i < UPDATE_OW; i++)
			{
				if (dataType[overwriteToUpdate[i]] != _STRING)
					p.SetData(overwriteToUpdate[i], currentData.GetData(overwriteToUpdate[i]));
				else
					p.SetDataStr(overwriteToUpdate[i], currentData.GetDataStr(overwriteToUpdate[i]));
			}
		//check AP state
		if (p.GetData(SPEED_CONST) != SPEED_CONST_MIN &&
		        currentData.GetData(SPEED_CONST) != SPEED_CONST_MIN)
			p.SetData(SPEED_CONST, currentData.GetData(SPEED_CONST));
	}
};

CommunicationManager Communication;
TaskManager Queue;

int isHandleZero = 0;


void process0()
{
	//
	int deviceState = Devices.GetState(0);
	isHandleZero++;
	if (deviceState == ACTIVE)
	{
		processData.SetData(MASTER_KEY, MASTER_KEY_ON);
	}
	else
	{
		processData.SetData(MASTER_KEY, MASTER_KEY_OFF);
	}
	return;
}

void process1()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(1);
	if (deviceState == ACTIVE)
	{
		processData.SetData(REVERSER, REVERSER_FORWARD);
	}
	else
	{
		processData.SetData(REVERSER, REVERSER_NEUTRAL);
	}
	return;
}

void process2()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(2);
	if (deviceState == ACTIVE)
	{
		processData.SetData(REVERSER, REVERSER_BACKWARD);
	}
	return;
}

void process3()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(3);
	if (deviceState == ACTIVE)
	{
		processData.SetData(HORN, HORN_ON);
	}
	else
	{
		processData.SetData(HORN, HORN_OFF);
	}
	return;
}

//-3
void process4()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(4);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 0);
		processData.SetData(BRAKE, 6);
	}
	return;
}

//-2
void process5()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(5);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 0);
		processData.SetData(BRAKE, 4);
	}
	return;
}

//-1
void process6()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(6);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 0);
		processData.SetData(BRAKE, 2);
	}
	return;
}

//1
void process7()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(7);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 1);
		processData.SetData(BRAKE, 0);
	}
	return;
}

//2
void process8()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(8);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 2);
		processData.SetData(BRAKE, 0);
	}
	return;
}

//3
void process9()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(9);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 3);
		processData.SetData(BRAKE, 0);
	}
	//
	if (isHandleZero > 600)
	{
		processData.SetData(POWER, 0);
		processData.SetData(BRAKE, 0);
		isHandleZero = 0;
	}
	return;
}

//key for bac horn -3 -2 -1 1 2 3 ld ldled rd rdled light pat sand

void process10()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(10);
	if (deviceState == ACTIVE)
	{
		if (processData.GetData(LDOOR_OPEN))return;
		processData.SetData(LDOOR_OPEN, 1);
	}
	else
	{
		if (!processData.GetData(LDOOR_OPEN))return;
		processData.SetData(LDOOR_OPEN, 0);
	}
}

void process11()
{
	//pinMode(devicePins[11], OUTPUT);
	digitalWrite(devicePins[11], processData.GetData(LDDOR_IN_OP));
}

void process12()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(12);
	if (deviceState == ACTIVE)
	{
		if (processData.GetData(RDOOR_OPEN))return;
		processData.SetData(RDOOR_OPEN, 1);
	}
	else
	{
		if (!processData.GetData(RDOOR_OPEN))return;
		processData.SetData(RDOOR_OPEN, 0);
	}
}

void process13()
{
	//pinMode(devicePins[13], OUTPUT);
	digitalWrite(devicePins[13], processData.GetData(RDOOR_IN_OP));
}

void process14()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(14);
	if (deviceState == ACTIVE)
	{
		if (processData.GetData(LIGHT_OPEN))return;
		processData.SetData(LIGHT_OPEN, 1);
	}
	else
	{
		if (!processData.GetData(LIGHT_OPEN))return;
		processData.SetData(LIGHT_OPEN, 0);
	}
}

void process15()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(15);
	if (deviceState == ACTIVE)
	{
		processData.SetData(PANTO_OPEN, !processData.GetData(PANTO_OPEN));
		while (Devices.GetState(15) == ACTIVE)
		{
			wdt_reset();
		}
		//while (Devices.GetState(14) == ACTIVE);
	}
}

void process16()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(16);
	if (deviceState == ACTIVE)
	{
		processData.SetData(SANDER_OPEN, 1);
	}
	else
	{
		processData.SetData(SANDER_OPEN, 0);
	}
}

void TimerInterrupt()
{
	FlexiTimer2::stop();
	Communication.SendDataToPC(currentData);
	Communication.RecieveDataFromPC(currentData);
	FlexiTimer2::start();
}

void setup()
{
	Serial.begin(115200);
	wdt_enable(WDTO_1S);
	FlexiTimer2::set(TIMER_TICK, TimerInterrupt);
	FlexiTimer2::start();
}

void loop()
{
	//state automatic
	Queue.GetLastState(processData);
	for (int i = 0; i < DEVICE_NUMBER; i++)
	{
		Process[i]();
		wdt_reset();
	}
	Queue.UpdateData(processData);
	Queue.AddProc(processData);
}
Source Code for Arduino DUEC/C++
/*Desktop Train Controller
===========================================
--Board: Arduino MEGA 2560
--Version: 1.0
--Simulator: OpenBVE
===========================================
--Note:
State Automatic Ready
===========================================
--Devices:
Type:0.SWITCH_C -> CHANGE
Type:1.SWITCH_F -> FALLING
Type:3.ENCODER -> CHANGE (in developing)
===========================================
--Train:
0.SPEED -> INT
1.REVERSER -> INT
2.POWER -> U8
3.BRAKE -> U8
4.SIGNAL -> INT
5.SIGNAL_DISTANCE -> INT
6.SPEED_LIMIT -> INT
7.HORN -> BOOL
8.SPEED_CONST -> INT
9.MASTER_KEY -> BOOL
===========================================
*/
#define SWITCH_C 0
#define SWITCH_F 1
#define ENCODER 2
#define DIG_OUT 3
#define ANALOG_OUT 4
#define DELAY_TIME 10
#define IGNORE -1
#define COUNT 2
#define KEY_UP 0
#define KEY_WAITTING 1
#define KEY_DOWN 2
#define SA_COUNT_SF 500
#define SA_COUNT_SC 300
//
#define SPEED_MIN 0
#define SPEED_MAX 400
#define REVERSER_FORWARD 1
#define REVERSER_NEUTRAL 0
#define REVERSER_BACKWARD -1
#define POWER_MIN 0
#define POWER_MAX 4
#define BRAKE_MIN 0
#define BRAKE_MAX 8
#define SIGNAL_RED 0
#define SIGNAL_YELLOW 1
#define SIGNAM_GREEN 2
#define SIGNAL_DISTANCE_N1 2000
#define SIGNAL_DISTANCE_N2 1500
#define SIGNAL_DISTANCE_N3 1000
#define SIGNAL_DISTANCE_N4 500
#define SIGNAL_DISTANCE_DE 0
#define SIGNAL_PASS 0
#define HORN_OFF 0
#define HORN_ON 1
#define SPEED_LIMIT_MIN 0
#define SPEED_LIMIT_DEF 30
#define SPEED_LIMIT_MAX 400
#define SPEED_CONST_MIN 0
#define SPEED_CONST_DEF 30
#define SPEED_CONST_MAX 400
#define MASTER_KEY_OFF 0
#define MASTER_KEY_ON 1
#define EMERGENCY_ON 1
#define EMERGENCY_OFF 0
#define OVERWRITE 0
#define NORMAL 1
//
#define LDOOR_OPEN_DEF 1
#define RDDOR_OPEN_DEF 1
#define SANDER_OFF 0
#define PANTO_UP 1
#define LIGHT_OFF 0
#define CURRENT_STATION_N 0
#define NEXT_STATION_N 0
#define NEXT_STATION_DIS_DEF 0
#define CURRENT_STATION_DEPART_TIME_DEF 0
#define NEXT_STATION_ARRIVAL_TIME_DEF 0
#define CURRENT_TIME_DEF 0
#define LDOOR_IN_OP_DEF 0
#define RDOOR_IN_OP_DEF 0
//Devices
#define DEVICE_NUMBER 17
#define DEVICE_TYPE_NUMBER 3
#define ACTIVE 0
#define NO_ACTIVE 1
#define NO_READY 0
#define READY 1
#define ON 1
#define OFF 0
#define ANALOG_OUT_MIN 0
#define ANALOG_OUT_MAX 255
//Train
#define TRAIN_DATA_NUMBER 25
#define _INT 0
#define _BOOL 1
#define _STRING 3
#define SPEED 0
#define REVERSER 1
#define POWER 2
#define BRAKE 3
#define SIGNAL_INFO 4
#define SIGNAL_DISTANCE 5
#define SPEED_LIMIT 6
#define HORN 7
#define SPEED_CONST 8
#define MASTER_KEY 9
#define EMERGENCY 10
#define RC_MODE 11
#define LDOOR_OPEN 12
#define RDOOR_OPEN 13
#define LIGHT_OPEN 15
#define PANTO_OPEN 16
#define SANDER_OPEN 14
#define CURRENT_STATION_NAME 17
#define NEXT_STATION_NAME 18
#define CURRENT_STATION_DEPART 20
#define NEXT_STATION_ARRIVAL 21
#define NEXT_STATION_DIS 19
#define CURRENT_TIME 22
#define LDDOR_IN_OP 23
#define RDOOR_IN_OP 24
#define UPDATE_LAST_NUM 14
#define UPDATE_OW 8
#define NO_BINDING -1
//HMI
#define HMI_SCRIPT_NUM 22
#define HMI_END_SYM 0xFF
#define MAX_SERIAL_STEP 5
//PC
#define FILTER '|'
#define START_SYM '#'
#define END_SYM '!'
#define NO_DATA ""
#define RECIEVE_DELAY 2
#define SEND_DELAY 25
#define TIMER_TICK 500000
//

#define NOP do { __asm__ __volatile__ ("nop"); } while (0)

#include "DueTimer.h"

typedef void (*funcPoint)();
//
/*
===========================================
--Device Maps
default:
PowerUp:process0
PowerDown:process1
ReserverForward:process2
ReserverBackward:process3
Horn:process4
SpeedConst:process5
Emergency:process6
MasterKey:process7
===========================================
*/

void process0();
void process1();
void process2();
void process3();
void process4();
void process5();
void process6();
void process7();
void process8();
void process9();
void process10();
void process11();
void process12();
void process13();
void process14();
void process15();
void process16();

//change device type here
const int deviceType[DEVICE_NUMBER] = {SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C, SWITCH_C,
                                       SWITCH_C, DIG_OUT, SWITCH_C, DIG_OUT, SWITCH_C, SWITCH_C, SWITCH_C
                                      };
//change functions here
const int devicePinsType[] = {INPUT_PULLUP, INPUT_PULLUP, INPUT_PULLUP, OUTPUT};
const funcPoint Process[DEVICE_NUMBER] = {process0, process1, process2, process3, process4, process5, process6, process7, process8, process9,
                                          process10, process11, process12, process13, process14, process15, process16
                                         };
//all use PULL_UP gpio mode
//key for bac horn -3 -2 -1 1 2 3 ld ldled rd rdled light pat sand
const int devicePins[DEVICE_NUMBER] = {31, 32, 33, 37, 25, 26, 27, 28, 29, 30, 39, 41, 22, 24, 35, 34, 38};
int deviceLastState[DEVICE_NUMBER] = {KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP,
                                      KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP
                                     };
int deviceSADur[DEVICE_NUMBER] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                  0, 0, 0, 0, 0, 0, 0
                                 };
//train data id
const int dataDefault[TRAIN_DATA_NUMBER] = {SPEED_MIN, REVERSER_NEUTRAL, POWER_MIN, BRAKE_MIN, SIGNAL_RED,
                                            SIGNAL_DISTANCE_DE, SPEED_LIMIT_DEF, HORN_OFF, SPEED_CONST_MIN, MASTER_KEY_OFF,
                                            EMERGENCY_OFF, NORMAL, LDOOR_OPEN_DEF, RDDOR_OPEN_DEF, SANDER_OFF, LIGHT_OFF, PANTO_UP, CURRENT_STATION_N, NEXT_STATION_N,
                                            NEXT_STATION_DIS_DEF, CURRENT_STATION_DEPART_TIME_DEF, NEXT_STATION_ARRIVAL_TIME_DEF, CURRENT_TIME_DEF, LDOOR_IN_OP_DEF, RDOOR_IN_OP_DEF
                                           };
const int dataType[TRAIN_DATA_NUMBER] = {_INT, _INT, _INT, _INT, _INT, _INT, _INT, _BOOL, _INT, _BOOL, _BOOL, _BOOL, _BOOL, _BOOL,
                                         _BOOL, _BOOL, _INT, _STRING, _STRING, _STRING, _STRING, _STRING, _STRING, _BOOL, _BOOL
                                        };
const int recieveToUpdate[UPDATE_LAST_NUM] = {SPEED, SIGNAL_INFO, SIGNAL_DISTANCE, SPEED_LIMIT, RC_MODE, LDDOR_IN_OP, RDOOR_IN_OP, NEXT_STATION_NAME, CURRENT_STATION_NAME,
                                              NEXT_STATION_DIS, NEXT_STATION_ARRIVAL, CURRENT_STATION_DEPART, NEXT_STATION_DIS, CURRENT_TIME
                                             };
const int overwriteToUpdate[UPDATE_OW] = {POWER, BRAKE, REVERSER, SPEED, SIGNAL_INFO, SIGNAL_DISTANCE, SPEED_LIMIT, RC_MODE};
const int dataBinding[TRAIN_DATA_NUMBER] = {NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING,
                                            NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING,
                                            NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING,
                                            NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING, NO_BINDING, 11, 13
                                           };

class TrainManager
{
public:
	int trainData[TRAIN_DATA_NUMBER];
	String trainDataStr[TRAIN_DATA_NUMBER];
	//did it has been sended
	TrainManager(const int dataDefault[])
	{
		//set default data to Train Manager
		for (int i = 0; i < TRAIN_DATA_NUMBER; i++)
		{
			trainData[i] = dataDefault[i];
			trainDataStr[i] = NO_DATA;
		}
	}
	//
	void SetData(int dataID, int value)
	{
		trainData[dataID] = value;
	}
	void SetDataStr(int dataID, String value)
	{
		trainDataStr[dataID] = value;
	}
	//get train data
	int GetData(int dataID)
	{
		return trainData[dataID];
	}
	String GetDataStr(int dataID)
	{
		return trainDataStr[dataID];
	}
};

class DevicesManager
{
public:
	DevicesManager(const int devicePins[], const int deviceType[], const int devicePinsType[])
	{
		//define gpio mode
		for (int i = 0; i < DEVICE_NUMBER; i++)
			pinMode(devicePins[i], devicePinsType[deviceType[i]]);
	}
	//
	int GetState(int deviceID)
	{
		if (deviceType[deviceID] == SWITCH_F)
		{
			if (digitalRead(devicePins[deviceID]) == LOW)
			{
				if (deviceLastState[deviceID] == KEY_UP)
				{
					deviceSADur[deviceID] = 0;
					deviceLastState[deviceID] = KEY_WAITTING;
				}
				else if (deviceLastState[deviceID] == KEY_WAITTING)
				{
					deviceSADur[deviceID]++;
					if (deviceSADur[deviceID] >= SA_COUNT_SF)
					{
						deviceSADur[deviceID] = 0;
						deviceLastState[deviceID] = KEY_DOWN;
						return ACTIVE;
					}
				}
			}
			else
			{
				deviceLastState[deviceID] = KEY_UP;
				return NO_ACTIVE;
			}
		}
		else if (deviceType[deviceID] == SWITCH_C)
		{
			if (digitalRead(devicePins[deviceID]) == LOW)
			{
				if (deviceLastState[deviceID] == KEY_UP)
				{
					deviceSADur[deviceID] = 0;
					deviceLastState[deviceID] = KEY_WAITTING;
				}
				else if (deviceLastState[deviceID] == KEY_WAITTING)
				{
					deviceSADur[deviceID]++;
					if (deviceSADur[deviceID] >= SA_COUNT_SC)
					{
						deviceSADur[deviceID] = 0;
						deviceLastState[deviceID] = KEY_DOWN;
						return ACTIVE;
					}
				}
				else if (deviceLastState[deviceID] == KEY_DOWN)return ACTIVE;
			}
			else
			{
				deviceLastState[deviceID] = KEY_UP;
				return NO_ACTIVE;
			}
		}
		return NO_ACTIVE;
	}
	//
	void delay_(int ms)
	{
		for (int i = 0; i < ms; i++)
		{
			for (int j = 0; j < 1985; j++) NOP;
		}
	}
};

DevicesManager Devices(devicePins, deviceType, devicePinsType);
TrainManager currentData(dataDefault);
TrainManager processData(dataDefault);

class CommunicationManager
{
private:
	String recieveData;
	String sendData;
	String sender;
	String tmp;
	int length, st, ed, pos, sendEd;
public:
	CommunicationManager()
	{
		Serial.begin(115200);
		//reset the recieved data
		sendData = NO_DATA;
		recieveData = NO_DATA;
		sender = NO_DATA;
		length = st = ed = pos = 0;
		sendEd = 0;
	}

	bool RecieveDataFromPC(TrainManager & p)
	{
		tmp = NO_DATA;
		//clear last data
		recieveData = NO_DATA;
		//recieve from serial
		while (Serial.available() > 0)
		{
			char currentRead = char(Serial.read());
			recieveData += currentRead;
			Devices.delay_(RECIEVE_DELAY);
			//if (currentRead == END_SYM)break;
		}
		length = recieveData.length();
		//no data exit
		if (!length)return false;
		//find start pos
		//Serial.println(recieveData);
		st = ed = 0;
		for (int i = 0; i < length; i++)
			if (recieveData.charAt(i) == START_SYM)
			{
				st = i;
				break;
			}
		//find end pos
		for (int i = length - 1; i >= 0; i--)
			if (recieveData.charAt(i) == END_SYM)
			{
				ed = i;
				break;
			}
		//cover data
		pos = 0;
		for (int i = st; i <= ed; i++)
		{
			if (recieveData.charAt(i) != START_SYM && recieveData.charAt(i) != END_SYM && recieveData.charAt(i) != FILTER)tmp += recieveData.charAt(i);
			if (recieveData.charAt(i) == FILTER)
			{
				//send data to TrainManager
				if (dataType[pos] != _STRING)
					p.SetData(pos++, tmp.toInt());
				else
				{
					p.SetDataStr(pos++, tmp);
				}
				//clear tmp
				tmp = NO_DATA;
			}
		}
		return true;
	}
	//
	bool SendDataToPC(TrainManager & p)
	{
		sendData = NO_DATA;
		//add start symbol
		sendData += START_SYM;
		//add contents
		for (int i = 0; i < TRAIN_DATA_NUMBER; i++)
		{
			if (dataType[i] != _STRING)sendData += p.GetData(i);
			else sendData += " ";
			sendData += FILTER;
		}
		//add end symbol
		sendData += END_SYM;
		if (!sendData.length())return false;
		//send data to PC
		Serial.print(sendData);
		return true;
	}
};

class TaskManager
{
public:
	TaskManager()
	{
		currentData = processData;
	}
	//
	void AddProc(TrainManager & p)
	{
		currentData = p;
	}
	//
	void GetLastState(TrainManager & p)
	{
		p = currentData;
	}
	//
	void UpdateData(TrainManager & p)
	{
		if (currentData.GetData(RC_MODE) == NORMAL)
			for (int i = 0; i < UPDATE_LAST_NUM; i++)
			{
				if (dataType[recieveToUpdate[i]] != _STRING)
					p.SetData(recieveToUpdate[i], currentData.GetData(recieveToUpdate[i]));
				else
					p.SetDataStr(recieveToUpdate[i], currentData.GetDataStr(recieveToUpdate[i]));
			}
		else if (currentData.GetData(RC_MODE) == OVERWRITE)
			for (int i = 0; i < UPDATE_OW; i++)
			{
				if (dataType[overwriteToUpdate[i]] != _STRING)
					p.SetData(overwriteToUpdate[i], currentData.GetData(overwriteToUpdate[i]));
				else
					p.SetDataStr(overwriteToUpdate[i], currentData.GetDataStr(overwriteToUpdate[i]));
			}
		//check AP state
		if (p.GetData(SPEED_CONST) != SPEED_CONST_MIN &&
		        currentData.GetData(SPEED_CONST) != SPEED_CONST_MIN)
			p.SetData(SPEED_CONST, currentData.GetData(SPEED_CONST));
	}
};

CommunicationManager Communication;
TaskManager Queue;

int isHandleZero = 0;


void process0()
{
	//
	int deviceState = Devices.GetState(0);
	isHandleZero++;
	if (deviceState == ACTIVE)
	{
		processData.SetData(MASTER_KEY, MASTER_KEY_ON);
	}
	else
	{
		processData.SetData(MASTER_KEY, MASTER_KEY_OFF);
	}
	return;
}

void process1()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(1);
	if (deviceState == ACTIVE)
	{
		processData.SetData(REVERSER, REVERSER_FORWARD);
	}
	else
	{
		processData.SetData(REVERSER, REVERSER_NEUTRAL);
	}
	return;
}

void process2()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(2);
	if (deviceState == ACTIVE)
	{
		processData.SetData(REVERSER, REVERSER_BACKWARD);
	}
	return;
}

void process3()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(3);
	if (deviceState == ACTIVE)
	{
		processData.SetData(HORN, HORN_ON);
	}
	else
	{
		processData.SetData(HORN, HORN_OFF);
	}
	return;
}

//-3
void process4()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(4);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 0);
		processData.SetData(BRAKE, 6);
	}
	return;
}

//-2
void process5()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(5);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 0);
		processData.SetData(BRAKE, 4);
	}
	return;
}

//-1
void process6()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(6);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 0);
		processData.SetData(BRAKE, 2);
	}
	return;
}

//1
void process7()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(7);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 1);
		processData.SetData(BRAKE, 0);
	}
	return;
}

//2
void process8()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(8);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 2);
		processData.SetData(BRAKE, 0);
	}
	return;
}

//3
void process9()
{
	//SWITCH_F
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF ||
	        processData.GetData(EMERGENCY) == EMERGENCY_ON ||
	        processData.GetData(REVERSER) == REVERSER_NEUTRAL ||
	        processData.GetData(SPEED_CONST) != SPEED_CONST_MIN)return;
	int deviceState = Devices.GetState(9);
	if (deviceState == ACTIVE)
	{
		isHandleZero --;
		processData.SetData(POWER, 3);
		processData.SetData(BRAKE, 0);
	}
	//
	if (isHandleZero > 600)
	{
		processData.SetData(POWER, 0);
		processData.SetData(BRAKE, 0);
		isHandleZero = 0;
	}
	return;
}

//key for bac horn -3 -2 -1 1 2 3 ld ldled rd rdled light pat sand

void process10()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(10);
	if (deviceState == ACTIVE)
	{
		if (processData.GetData(LDOOR_OPEN))return;
		processData.SetData(LDOOR_OPEN, 1);
	}
	else
	{
		if (!processData.GetData(LDOOR_OPEN))return;
		processData.SetData(LDOOR_OPEN, 0);
	}
}

void process11()
{
	pinMode(devicePins[11], OUTPUT);
	digitalWrite(devicePins[11], processData.GetData(LDDOR_IN_OP));
}

void process12()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(12);
	if (deviceState == ACTIVE)
	{
		if (processData.GetData(RDOOR_OPEN))return;
		processData.SetData(RDOOR_OPEN, 1);
	}
	else
	{
		if (!processData.GetData(RDOOR_OPEN))return;
		processData.SetData(RDOOR_OPEN, 0);
	}
}

void process13()
{
	pinMode(devicePins[13], OUTPUT);
	digitalWrite(devicePins[13], processData.GetData(RDOOR_IN_OP));
}

void process14()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(14);
	if (deviceState == ACTIVE)
	{
		if (processData.GetData(LIGHT_OPEN))return;
		processData.SetData(LIGHT_OPEN, 1);
	}
	else
	{
		if (!processData.GetData(LIGHT_OPEN))return;
		processData.SetData(LIGHT_OPEN, 0);
	}
}

void process15()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(15);
	if (deviceState == ACTIVE)
	{
		processData.SetData(PANTO_OPEN, !processData.GetData(PANTO_OPEN));
		delay(1000);
		//while (Devices.GetState(14) == ACTIVE);
	}
}

void process16()
{
	if (processData.GetData(MASTER_KEY) == MASTER_KEY_OFF)return;
	int deviceState = Devices.GetState(16);
	if (deviceState == ACTIVE)
	{
		processData.SetData(SANDER_OPEN, 1);
	}
	else
	{
		processData.SetData(SANDER_OPEN, 0);
	}
}

void TimerInterrupt()
{
	Timer1.stop();
	Communication.SendDataToPC(currentData);
	Communication.RecieveDataFromPC(currentData);
	Timer1.start();
}

void setup()
{
	Serial.begin(115200);
	Timer1.attachInterrupt(TimerInterrupt);
	Timer1.setPeriod(TIMER_TICK);
	Timer1.start();
}

void loop()
{
	//state automatic
	Queue.GetLastState(processData);
	for (int i = 0; i < DEVICE_NUMBER; i++)Process[i]();
	Queue.UpdateData(processData);
	Queue.AddProc(processData);
}
Controller Arduino Source Code on Github
GitHub

Custom parts and enclosures

Power / Brake Combien Handle Design
DXF files on GitHub

Schematics

Comments

Similar projects you might like

Automated NERF Gun Shooting Gallery

Project showcase by Keegan Neave

  • 3,095 views
  • 1 comment
  • 15 respects

Otto DIY+ Arduino Bluetooth Robot Easy to 3D Print

Project tutorial by Team Otto builders

  • 54,019 views
  • 122 comments
  • 182 respects

Arduino Environmental Monitoring

Project showcase by Prajay Basu

  • 13,743 views
  • 1 comment
  • 25 respects

IoT for coins

Project tutorial by Erik Moran

  • 8,372 views
  • 1 comment
  • 42 respects

Ghostbusters Trap Armband

Project showcase by Danielle Gormley

  • 970 views
  • 0 comments
  • 4 respects

Multi-Dashboard Display with Arduino Controller

Project showcase by Colin O'Dell

  • 25,768 views
  • 6 comments
  • 70 respects
Add projectSign up / Login