Sam Farah
Published

Flight Simulator Custom Controls

DIY custom flight simulator panels that allow a player to control their planes with a nice compact desktop panel.

IntermediateShowcase (no instructions)15,689
Flight Simulator Custom Controls

Things used in this project

Hardware components

ATmega328
Microchip ATmega328
×4
AS1115
×10
FT232RL
×4
STMicroelectronics M74HC595YTTR
×10

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

Autopilot Firmware

Arduino
/****************************************************************************************/
/*																						*/
/*							Autopilot 500-TS | ©Kantooya Inc							*/
/*																						*/
/*		Author:		Sam Farah															*/
/*		Email:		sam.farah1986@gmail.com												*/
/*		Website:	http://samfarah.net													*/
/*		Version:	1.0																	*/
/*		Description:																	*/
/*				The firmware for the ATMega microcontroller on board, it controls		*/
/*				the circuit, and handles reading inputs and sends commands to FSX.		*/
/*																						*/
/****************************************************************************************/

//-------------------------- Headers ----------------------------
#include <AS1115.h>
#include "WSWire.h"
#include "math.h"
#include "Quadrature.h" //for rotary encoders
//---------------------------------------------------------------

//---------------------------- Pins -----------------------------
//			Name			Arduino Pin				Pin on ATMega
//		-----------			-----------				-------------
#define LED_DS					2			//			32
#define LED_CLK					3			//			1
#define LED_LTCH				4			//			2 
#define ALTEnc_B				6			//
#define ALTEnc_A				7			//
#define VSEnc_B					8			//
#define VSEnc_A					9			//
#define IRQ						13			//			17 
#define SpdEnc_B				10			//
#define SpdEnc_A				11			//
#define HdgEnc_A				A0			//
#define HdgEnc_B				A1			//
#define CrsEnc_A				A2			//
#define CrsEnc_B				A3			//
#define SDA						A4			//
#define SCL						A5			//
//---------------------------------------------------------------

//------------------------- Settings ----------------------------
#define SerialTimeout			1000
#define TempDelay				2000
//---------------------------------------------------------------

//-------------------------- Enums ------------------------------
enum EDispID{ ESpeedDisp = 0, EALTDisp = 1, EVSDisp = 2, ECrsDsip = 3, EHdgDisp = 4 };
//---------------------------------------------------------------

//----------------------- Prototypes ----------------------------
int ReadSwitches();
void pciSetup(byte);
void SendLEDData(String);
void(*resetFunc) (void) = 0; //declare reset function @ address 0
void TestLEDs(int);
void InitLEDs(int);
uint8_t I2C_ClearBus();
class SDisplayData;
class TTestDisplayData;
class TDisplay;
class TEncoderData;
class TBlinkLED;
//---------------------------------------------------------------

//------------------------- Objects -----------------------------
AS1115 DispCtrl1 = AS1115(0x01);
AS1115 DispCtrl2 = AS1115(0x02);
AS1115 SwitchCtrl = AS1115(0x03);
TEncoderData *SpeedCtrl, *ALTCtrl, *VSCtrl, *HdgCtrl, *CrsCtrl;
TBlinkLED *RealDataBlink, *SpeedBlink, *AltBlink, *HdgBlink;
//---------------------------------------------------------------
class SDisplayData
{
public:
	String SetMem;
	String ReadMem;
};
//---------------------- Public Variables -----------------------
int SwLEDList[] = { 3, 1, 2, 0, 9, 10, 11, 4, 0, 0, 14, 5, 0, 12, 8, 13 };
bool BlinIndexArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
String FSXCommands[] = { "B09", "B04", "B10", "", "A54", "A54", "B30", "B08", "", "", "Y080", "B05", "", "B34", "B01", "B26" };
String LEDString = "0000000000000000";
String OldLEDs = "0000000000000000";
String  BlinkLEDs = "0000000000000000";
int tempVal, CodeIn;
bool TestProgram = false, ShowDP = false, ShowRealValues = false, OldShowRealValues = false;
bool ShowZeros, Connected = false, MasterBattery;
char navMeM, SpeedMeM, ATHRMeM;
SDisplayData ALTData, SpdData, VSData, HdgData, CrsData;
TDisplay *Displays[5];
unsigned long int HoldTime = 0, TimeOutCheck;
//---------------------------------------------------------------

//-------------------------- Classes ----------------------------
class TDisplay
{
private:
	byte *Index;
	int DigitCount;
	EDispID ID;
	bool TempValChange;
	unsigned long int LastTempChange;
	SDisplayData *Data;
	void SendValues(byte index, char value)
	{
		if (index > 7)
		{
			if (DispCtrl2.digitWrite(index - 8, value, (TestProgram ? ShowZeros : MasterBattery)))
			{
				Serial.println("I2C Froze (Displays)");
				I2C_ClearBus();
				Wire.begin();
				DispCtrl1.begin();
				DispCtrl2.begin();
				SwitchCtrl.begin();
			}
		}
		else
		{
			if (DispCtrl1.digitWrite(index, value, (index == 6 || index == 1 ? ShowDP : 0)))
			{
				Serial.println("I2C Froze (Displays)");
				I2C_ClearBus();
				Wire.begin();
				DispCtrl1.begin();
				DispCtrl2.begin();
				SwitchCtrl.begin();
			}
		}
	}
public:
	TDisplay(byte *_Index, EDispID _ID, SDisplayData *_Data)
	{
		Index = _Index;
		DigitCount = 3;
		ID = _ID;
		UpdateDisplay("   ");
		TempValChange = false;
		Data = _Data;
	}
	void CheckTempDisplay()
	{
		if (TempValChange && millis() - LastTempChange > TempDelay)
		{
			TempValChange = false;
			UpdateDisplay();
		}
	}

	void UpdateDisplay()
	{
		UpdateDisplay(!ShowRealValues || TempValChange ? Data->SetMem : Data->ReadMem);
	}
	void UpdateDisplay(String Val)
	{


		bool LeadZeroFound = false;
		bool Negative = false;
		for (int i = 0; i < DigitCount; i++)
		{
			if (!MasterBattery && !TestProgram)Val[i] = ' ';
			else
			{
				if (Val[i] == '-')
				{
					Negative = true;
					Val[i] = ' ';
				}
				else if (Val[i] == '+')Val[i] = ' ';
				else
					if (Val[i] == '0' && !LeadZeroFound && i != DigitCount - 1 && (!TestProgram || ((Index[i] != 6 && Index[i] != 1) || LEDString[11] == '0')))Val[i] = ' ';
					else
					{
						LeadZeroFound = true;
						if (Negative)
						{
							if (TestProgram && (LEDString[11] == '1' && (Index[i] == 7 || Index[i] == 2)))
							{
								SendValues(Index[i - 1], '0');
								SendValues(Index[i - 2], '-');
							}
							else SendValues(Index[i - 1], '-');

							Negative = false;
						}
					}
			}

			SendValues(Index[i], Val[i]);
		}
	}
	void ClearDisplay()
	{
		UpdateDisplay("   ");
	}
	EDispID GetID(){ return ID; }
	void SetTempValChange(bool val)
	{
		TempValChange = val;
		LastTempChange = millis();
	}

};
//--------------------------------------------

//--------------------------------------------
class TTestDisplayData
{
public:
	int Value;
	TTestDisplayData()
	{
		Value = 0;
	}
	String toString()
	{
		String RetVal = String(Value);
		String Buff = "";
		for (int i = 0; i < 3 - RetVal.length(); i++)
			Buff += '0';
		RetVal = Buff + RetVal;

		return RetVal;
	}
	void Inc()
	{
		Value++;
		if (Value>999)Value = 0;
	}
	void Dec(bool AllowNegative)
	{
		Value--;
		if (AllowNegative){ if (Value < -99)Value = 0; }
		else{ if (Value < 0)Value = 0; }
	}
};
//--------------------------------------------
class TEncoderData
{
private:
	int R;// a variable
	int Rold;// the old reading
	int Rdif;// the difference since last loop
	Quadrature *Encoder;
	TTestDisplayData *TestData;
	TDisplay *Disp;

public:
	TEncoderData(int _PinA, int _PinB, TDisplay *_Disp)
	{
		Encoder = new Quadrature(_PinA, _PinB);
		TestData = new TTestDisplayData();
		Disp = _Disp;
	}
	void ReadEncoder(String IncCommand, String DecCommand)
	{
		R = (Encoder->position() / 2); //The /2 is to suit the encoder
		if (R != Rold) { // checks to see if it different
			(Rdif = (R - Rold));// finds out the difference
			if (Rdif == 1) Serial.println(IncCommand);
			if (Rdif == -1)Serial.println(DecCommand);
			Rold = R; // overwrites the old reading with the new one.
			Disp->SetTempValChange(true);
		}
	}
	void ReadEncoder(TDisplay *Disp)//used for test program.
	{
		R = (Encoder->position() / 2); //The /2 is to suit the encoder
		if (R != Rold) { // checks to see if it different
			(Rdif = (R - Rold));// finds out the difference
			if (Rdif == 1)TestData->Inc();
			if (Rdif == -1)TestData->Dec(true);
			Rold = R; // overwrites the old reading with the new one.
			Disp->UpdateDisplay(TestData->toString());
		}
	}
	void RefreshDisplay(TDisplay *Disp)
	{
		Disp->UpdateDisplay(TestData->toString());
	}
};
//--------------------------------------------
class TBlinkLED
{
private:
	unsigned long  BlinkDelayCounter;
	int LEDIndex;
	int NormalDelay;
	int ActiveDelay;
	bool BlinkTemp;
public:
	void Activate()
	{
		BlinkTemp = true;
		BlinkDelayCounter = 0;

	}
	void deactivate()
	{
		BlinkTemp = false;
	}
	TBlinkLED(int _LEDIndex, int _NormalDelay, int _ActiveDelay)
	{
		LEDIndex = _LEDIndex;
		NormalDelay = _NormalDelay;
		ActiveDelay = _ActiveDelay;
		BlinIndexArray[LEDIndex] = true;
	}
	void Blink(bool pressed)
	{
		if (millis() - BlinkDelayCounter > (pressed ? ActiveDelay : NormalDelay))
		{
			BlinkLEDs = GetBlinkString();
			BlinkLEDs[LEDIndex] = BlinkTemp ? '1' : '0';
			SendLEDData(BlinkLEDs);
			BlinkTemp = !BlinkTemp;
			BlinkDelayCounter = millis();
		}
	}
	void DontBlink()
	{
		BlinkLEDs = GetBlinkString();
		BlinkLEDs[LEDIndex] = LEDString[LEDIndex];
		if (BlinkLEDs != OldLEDs)
		{
			SendLEDData(BlinkLEDs);
			OldLEDs = BlinkLEDs;
		}
	}
};
//--------------------------------------------
//----------------------- End of Classes ------------------------
String GetBlinkString()
{
	String RetVal = "0000000000000000";
	for (int i = 0; i < 16; i++)
	{
		if (!BlinIndexArray[i])RetVal[i] = LEDString[i];
		else RetVal[i] = BlinkLEDs[i];
	}
	return RetVal;
}
//----------------------- Interrupts ----------------------------
ISR(PCINT0_vect) // handle pin change interrupt for D8 to D13 here
{
	interrupts();
	if (digitalRead(IRQ) == LOW)
	{

		tempVal = ReadSwitches();
		if (tempVal > 0)
		{
			if (TestProgram) //Execute Test Program when buttons are pressed.
			{
				if ((LEDString[4] != '1' || tempVal == 8) && tempVal != 1)
				{
					LEDString[SwLEDList[tempVal - 1]] = (LEDString[SwLEDList[tempVal - 1]] == '0' ? '1' : '0');
					SendLEDData(ShowRealValues ? BlinkLEDs : LEDString);
				}
				switch (tempVal)
				{
				case 1:
					HoldTime = millis();
					break;
				case 13:
					resetFunc();  //exit test program
					break;
				case 5:
				case 6:
				case 7:
				case 14:
				case 15:
					ShowZeros = (LEDString[12] == '1');
					ShowDP = (LEDString[11] == '1'&& LEDString[8] == '1' && LEDString[5] == '0');
					if (LEDString[9] == '0' && LEDString[10] == '1' && LEDString[8] == '1')
					{
						DispCtrl1.setFont(FONT_CODEB);
						DispCtrl2.setFont(FONT_CODEB);
						SpeedCtrl->RefreshDisplay(Displays[ESpeedDisp]);
						ALTCtrl->RefreshDisplay(Displays[EALTDisp]);
						VSCtrl->RefreshDisplay(Displays[EVSDisp]);
						CrsCtrl->RefreshDisplay(Displays[ECrsDsip]);
						HdgCtrl->RefreshDisplay(Displays[EHdgDisp]);
					}
					break;
				case 8:
					if (LEDString[4] == '1')
					{
						ShowRealValues = false;
						LEDString = "1111111111111111";
						for (int i = 0; i < 8; i++)DispCtrl1.digitWrite(i, 8, (i == 6 || i == 1));
						for (int i = 0; i < 7; i++)DispCtrl2.digitWrite(i, 8, 1);
					}
					else
					{
						ShowDP = false;
						LEDString = "0000000000000000";
					}
					SendLEDData(LEDString);
					break;
				}
			}
			else if (Connected) //send commands to FSX 
			{
				switch (tempVal)
				{
				case 1: //B/C Hold Button
					HoldTime = millis();
					break;
				case 2: //heading pressed, fixed bug where it captures real data.
					Serial.println(LEDString[1] == '0' ? "Y111" : "Y121");
					break;
				case 12:// alt pressed, fixed bug where it captures real data
					Serial.println(LEDString[5] == '0' ? "Y091" : "Y101");
					break;
				case 13://Level Button
					Serial.println("B05");
					Serial.println("B05");
					Serial.println("B210000");
					break;
				default:
					Serial.println(FSXCommands[tempVal - 1]);
				}
			}
		}
	}
	else if (Connected || TestProgram)
	{
		if (tempVal == 1)
		{
			if (millis() - HoldTime > 300)
			{
				BlinkLEDs = LEDString;
				RealDataBlink->Activate();
				SpeedBlink->Activate();
				AltBlink->Activate();
				HdgBlink->deactivate();

				for (int i = 0; i < 5; i++)Displays[i]->SetTempValChange(false);
				
				ShowRealValues = !ShowRealValues;
				if (TestProgram){
					if (!ShowRealValues)
					{
						LEDString[3] == '0';
						SendLEDData(LEDString);
					}
				}
			}
			else
			{
				if (TestProgram)
				{
					LEDString[3] = LEDString[3] == '0' ? '1' : '0';
					SendLEDData(LEDString);
				}
				else
					Serial.println("B09");
			}
		}
	}
}
//---------------------------------------------------------------
void setup() {

	Serial.begin(115200);
	pinMode(IRQ, INPUT);
	pinMode(LED_DS, OUTPUT);
	pinMode(LED_CLK, OUTPUT);
	pinMode(LED_LTCH, OUTPUT);

	DispCtrl1.setFont(FONT_CODEB);
	DispCtrl2.setFont(FONT_CODEB);
	DispCtrl1.setDecode(DECODE_ALL_FONT);
	DispCtrl2.setDecode(DECODE_ALL_FONT);

	MasterBattery = false;

	Displays[ESpeedDisp] = new TDisplay(new byte[3]{3, 4, 5}, ESpeedDisp, &SpdData);;
	Displays[EALTDisp] = new TDisplay(new byte[3]{0, 1, 2}, EALTDisp, &ALTData);;
	Displays[EVSDisp] = new TDisplay(new byte[3]{14, 6, 7}, EVSDisp, &VSData);;
	Displays[ECrsDsip] = new TDisplay(new byte[3]{8, 9, 10}, ECrsDsip, &CrsData);;
	Displays[EHdgDisp] = new TDisplay(new byte[3]{11, 12, 13}, EHdgDisp, &HdgData);;

	SpeedCtrl = new TEncoderData(SpdEnc_A, SpdEnc_B, Displays[ESpeedDisp]);
	ALTCtrl = new TEncoderData(ALTEnc_A, ALTEnc_B, Displays[EALTDisp]);
	VSCtrl = new TEncoderData(VSEnc_A, VSEnc_B, Displays[EVSDisp]);
	HdgCtrl = new TEncoderData(HdgEnc_A, HdgEnc_B, Displays[EHdgDisp]);
	CrsCtrl = new TEncoderData(CrsEnc_A, CrsEnc_B, Displays[ECrsDsip]);

	RealDataBlink = new TBlinkLED(3, 250, 125);
	SpeedBlink = new TBlinkLED(13, 125, 250);
	AltBlink = new TBlinkLED(5, 130, 250);
	HdgBlink = new TBlinkLED(1, 135, 250);

	SwitchCtrl.begin();
	DispCtrl1.begin();
	DispCtrl2.begin();
	delay(500);

	DispCtrl1.setIntensity(0x0F);
	DispCtrl2.setIntensity(0x0F);

	InitLEDs(1);

	if (SwitchCtrl.ReadKeys(NULL) == 13)//go to test program
	{
		TestProgram = true;
		TestLEDs(50);
		SendLEDData("0000000000000000");
	}
	pciSetup(IRQ);

	if (TestProgram)InitLEDs(2);
}
//------------------------------------------------------------------------------------------------------------------------------------------
void loop() {
	if (TestProgram)
	{
		if (LEDString[4] == '0')
		{
			if (LEDString[8] == '1')
			{
				if (LEDString[9] == '1')
				{
					for (int i = 0; i < 5; i++)Displays[i]->UpdateDisplay(String(random(0, 1000)));
					delay(200);
				}
				else if (LEDString[10] == '1')
				{
					SpeedCtrl->ReadEncoder(Displays[ESpeedDisp]);
					ALTCtrl->ReadEncoder(Displays[EALTDisp]);
					VSCtrl->ReadEncoder(Displays[EVSDisp]);
					CrsCtrl->ReadEncoder(Displays[ECrsDsip]);
					HdgCtrl->ReadEncoder(Displays[EHdgDisp]);
				}
				else {
					DispCtrl1.setFont(FONT_HEX);
					DispCtrl2.setFont(FONT_HEX);

					for (int i = 0; i < 8; i++)
						if (DispCtrl1.digitWrite(i, i, 0) >= 4)
						{
							Serial.println("I2C Froze");
							I2C_ClearBus();
							Wire.begin();
							DispCtrl1.begin();
							DispCtrl2.begin();
						}
					for (int i = 0; i < 7; i++)
						if (DispCtrl2.digitWrite(i, i + 8, ShowZeros) >= 4)
						{
							Serial.println("I2C Froze");
							I2C_ClearBus();
							Wire.begin();
							DispCtrl1.begin();
							DispCtrl2.begin();
						}
					delay(200);
				}
			}
			else
			{
				DispCtrl1.setFont(FONT_CODEB);
				DispCtrl2.setFont(FONT_CODEB);
				ShowZeros = false;
				for (int i = 0; i < 5; i++)Displays[i]->ClearDisplay();
				delay(200);
			}
		}
		if (ShowRealValues)
		{
			RealDataBlink->Blink(LEDString[3] == '1');
			if (LEDString[13] == '1')SpeedBlink->Blink(false);
			if (LEDString[5] == '1')AltBlink->Blink(false);
			if (LEDString[1] == '1')HdgBlink->Blink(false);
		}
	}
	else //real program
	{
		{ENCODER(); } //Check the Rotary Encoders
		if (Serial.available())
		{  //Check if anything there
			CodeIn = getChar();      //Get a serial read if there is.
			if (CodeIn == '=') { EQUALS();   Connected = true; } // The first identifier is "=" ,, goto void EQUALS
			else if (CodeIn == '?') { QUESTION(); Connected = true; }// The first identifier is "?" ,, goto void QUESTION
			else if (CodeIn == '<') { LESSTHAN(); Connected = true; }// The first identifier is "?" ,, goto void QUESTION
			else
			{
				String ReadBuffer = "";
				while (Serial.available())ReadBuffer += char(Serial.read());// Empties the buffer use to show ReadBuffer += char(Serial.read());
				if (ReadBuffer.length() > 0)
				{
					Serial.println("--------------------------");
					Serial.println(ReadBuffer);
					Serial.println("--------------------------");
				}
			}
			for (int i = 0; i < 5; i++)Displays[i]->UpdateDisplay();
		}

		if (ShowRealValues != OldShowRealValues)
		{
			OldShowRealValues = ShowRealValues;
			for (int i = 0; i < 5; i++)Displays[i]->UpdateDisplay();
		}

		if (ShowRealValues && MasterBattery)
		{
			RealDataBlink->Blink(LEDString[3] == '1');

			if (LEDString[13] == '1' && SpdData.ReadMem != SpdData.SetMem)SpeedBlink->Blink(false);
			else SpeedBlink->DontBlink();

			if (LEDString[5] == '1' &&   VSData.ReadMem.substring(1, 3) != "00")AltBlink->Blink(false);
			else AltBlink->DontBlink();

			if (LEDString[1] == '1' && HdgData.ReadMem != HdgData.SetMem && !(HdgData.ReadMem == "360" && HdgData.SetMem == "000"))HdgBlink->Blink(false);
			else HdgBlink->DontBlink();
		}
		else
			if (LEDString != OldLEDs)
			{
				SendLEDData(LEDString);
				OldLEDs = LEDString;
			}

		for (int i = 0; i < 5; i++)Displays[i]->CheckTempDisplay();
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
char getChar()// Get a character from the serial buffer
{
	TimeOutCheck = millis();
	while (Serial.available() == 0)
		if (millis() - TimeOutCheck > SerialTimeout)return '0';//time out command
	return((char)Serial.read());
}
//------------------------------------------------------------------------------------------------------------------------------------------
void SendLEDData(String data)
{
	digitalWrite(LED_LTCH, HIGH);
	digitalWrite(LED_LTCH, LOW);
	digitalWrite(LED_LTCH, HIGH);
	for (int i = 15; i >= 0; i--)
	{
		digitalWrite(LED_DS, (data[i] == '1' ? HIGH : LOW));
		digitalWrite(LED_CLK, LOW);
		digitalWrite(LED_CLK, HIGH);
	}
	digitalWrite(LED_LTCH, LOW);
	digitalWrite(LED_LTCH, HIGH); //fix for double send
	digitalWrite(LED_LTCH, LOW);

}
//------------------------------------------------------------------------------------------------------------------------------------------
void pciSetup(byte pin)
{
	*digitalPinToPCMSK(pin) |= bit(digitalPinToPCMSKbit(pin));  // enable pin
	PCIFR |= bit(digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
	PCICR |= bit(digitalPinToPCICRbit(pin)); // enable interrupt for the group
}
//------------------------------------------------------------------------------------------------------------------------------------------
int ReadSwitches()
{
	uint8_t ErrorCode;
	int val = SwitchCtrl.ReadKeys(&ErrorCode);
	if (ErrorCode >= 4)
	{
		Serial.println("I2C Froze (Switches)");
		I2C_ClearBus();
		Wire.begin();
		DispCtrl1.begin();
		DispCtrl2.begin();
		SwitchCtrl.begin();
	}
	if (val > 0 && val < 255)
		return val;
	return 0;
}
//------------------------------------------------------------------------------------------------------------------------------------------
void TestLEDs(int D)
{
	for (int i = 1; i < 15; i++)
	{
		if (i == 6 || i == 7 || i == 15 || i == 0)continue;
		String temp = "";
		for (int j = 0; j < 16; j++) temp += (j == i ? "1" : "0");
		SendLEDData(temp);
		delay(D);
	}
	for (int i = 13; i >= 2; i--)
	{
		if (i == 6 || i == 7 || i == 15 || i == 0)continue;
		String temp = "";
		for (int j = 0; j < 15; j++) temp += (j == i ? "1" : "0");
		SendLEDData(temp);
		delay(D);
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void InitLEDs(int count)
{
	int _Delay = 300;
	for (int i = 1; i <= count; i++)
	{
		SendLEDData("1111111111111111");
		delay(_Delay);
		SendLEDData("0000000000000000");
		delay(_Delay);
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void EQUALS(){
	CodeIn = getChar();
	switch (CodeIn) {
	case 'a':	LEDString[8] = getChar();		break;	//Master AP
	case 'k':	LEDString[5] = getChar();		break;	//Altitude Hold
	case 'm':	LEDString[4] = getChar();		break;	//App hold
	case 'o':	LEDString[2] = getChar();		break;	//Loc (Nav) Hold
	case 'n': 	LEDString[3] = getChar();		break;	//B/C hold
	case 'j':	LEDString[1] = getChar();		break;	//Heading Hold
	case 'q':	LEDString[11] = getChar();		break;	//Flight Director
	case 'v':	LEDString[14] = getChar();		break;	//Take-Off Power
	case 'b':	ALTData.SetMem = GetBufferValue(5);	break;	//ALT Set	
	case 'f':	SpdData.SetMem = GetBufferValue(3);	break;	//Speed Set		
	case 'c':	VSData.SetMem = GetBufferValue(5);	break;	//Vertical Speed Set
	case 'd':	HdgData.SetMem = GetBufferValue(3);	break;	//Heading Set
	case 'e':	CrsData.SetMem = GetBufferValue(3); CrsData.ReadMem = CrsData.SetMem; break;	//Course Set
	case 't':											//ATHR
		ATHRMeM = getChar();
		if (MasterBattery)LEDString[12] = ATHRMeM;
		break;
	case 'l':											//GPS-NAV
		navMeM = getChar();
		if (MasterBattery)
		{
			LEDString[9] = navMeM;
			LEDString[10] = (LEDString[9] == '0' ? '1' : '0');
		}
		break;
	case 's':											//Speed Hold
		SpeedMeM = getChar();
		if (MasterBattery)LEDString[13] = SpeedMeM;
		break;
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void QUESTION(){    // The first identifier was "?"
	CodeIn = getChar();
	switch (CodeIn) {
	case 'U': // Bus Voltage
		String buff = GetBufferValue(2);
		getChar();
		getChar();
		if (buff.toInt() == 0)//plain's turned off
		{
			MasterBattery = false;
			LEDString[9] = '0';
			LEDString[10] = '0';
			LEDString[12] = '0';
			LEDString[13] = '0';
			for (int i = 0; i < 5; i++)Displays[i]->UpdateDisplay("000");
		}
		else
		{
			MasterBattery = true;
			LEDString[9] = navMeM;
			LEDString[10] = (LEDString[9] == '0' ? '1' : '0');
			LEDString[12] = ATHRMeM;
			LEDString[13] = SpeedMeM;
			for (int i = 0; i < 5; i++)Displays[i]->UpdateDisplay();
		}
	}
}
void LESSTHAN()
{
	CodeIn = getChar();
	switch (CodeIn)
	{
	case 'D':	ALTData.ReadMem = GetBufferValue(5);		break;		//ALT Read	
	case 'P':	SpdData.ReadMem = GetBufferValue(3);		break;		//Speed Read
	case 'J':	HdgData.ReadMem = GetBufferValue(3);		break;		//Heading Read		
	case 'L':													//Vertical Speed Read
		VSData.ReadMem = GetBufferValue(6);
		VSData.ReadMem = VSData.ReadMem[0] + VSData.ReadMem.substring(2, 6);
		break;
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void ENCODER(){

	SpeedCtrl->ReadEncoder("B15", "B16");
	ALTCtrl->ReadEncoder("B11", "B12");
	VSCtrl->ReadEncoder("B13", "B14");
	HdgCtrl->ReadEncoder("A57", "A58");
	CrsCtrl->ReadEncoder("A56", "A55");
}
//------------------------------------------------------------------------------------------------------------------------------------------
String GetBufferValue(int byteCount)
{
	String buff = "";
	char temp;
	for (int i = 0; i < byteCount; i++)
	{
		temp = getChar();
		if ((temp <= '9' && temp >= '0') || temp == '-' || temp == '+' || temp == '.')
			buff += temp;
		else
			buff += '0';
	}

	//Serial.println(buff);

	return buff;
}
//------------------------------------------------------------------------------------------------------------------------------------------
uint8_t I2C_ClearBus() {
#if defined(TWCR) && defined(TWEN)
	TWCR &= ~(_BV(TWEN)); //Disable the Atmel 2-Wire interface so we can control the SDA and SCL pins directly
#endif

	pinMode(SDA, INPUT_PULLUP); // Make SDA (data) and SCL (clock) pins Inputs with pullup.
	pinMode(SCL, INPUT_PULLUP);
	delay(200);

	boolean SCL_LOW = (digitalRead(SCL) == LOW); // Check is SCL is Low.
	if (SCL_LOW) { //If it is held low Arduno cannot become the I2C master.
		return 1; //I2C bus error. Could not clear SCL clock line held low
	}

	boolean SDA_LOW = (digitalRead(SDA) == LOW);  // vi. Check SDA input.
	int clockCount = 20; // > 2x9 clock

	while (SDA_LOW && (clockCount > 0)) { //  vii. If SDA is Low,
		clockCount--;
		// Note: I2C bus is open collector so do NOT drive SCL or SDA high.
		pinMode(SCL, INPUT); // release SCL pullup so that when made output it will be LOW
		pinMode(SCL, OUTPUT); // then clock SCL Low
		delayMicroseconds(10); //  for >5uS
		pinMode(SCL, INPUT); // release SCL LOW
		pinMode(SCL, INPUT_PULLUP); // turn on pullup resistors again
		// do not force high as slave may be holding it low for clock stretching.
		delayMicroseconds(10); //  for >5uS
		// The >5uS is so that even the slowest I2C devices are handled.
		SCL_LOW = (digitalRead(SCL) == LOW); // Check if SCL is Low.
		int counter = 20;
		while (SCL_LOW && (counter > 0)) {  //  loop waiting for SCL to become High only wait 2sec.
			counter--;
			delay(100);
			SCL_LOW = (digitalRead(SCL) == LOW);
		}
		if (SCL_LOW) { // still low after 2 sec error
			return 2; // I2C bus error. Could not clear. SCL clock line held low by slave clock stretch for >2sec
		}
		SDA_LOW = (digitalRead(SDA) == LOW); //   and check SDA input again and loop
	}
	if (SDA_LOW) { // still low
		return 3; // I2C bus error. Could not clear. SDA data line held low
	}

	// else pull SDA line low for Start or Repeated Start
	pinMode(SDA, INPUT); // remove pullup.
	pinMode(SDA, OUTPUT);  // and then make it LOW i.e. send an I2C Start or Repeated start control.
	// When there is only one I2C master a Start or Repeat Start has the same function as a Stop and clears the bus.
	/// A Repeat Start is a Start occurring after a Start with no intervening Stop.
	delayMicroseconds(10); // wait >5uS
	pinMode(SDA, INPUT); // remove output low
	pinMode(SDA, INPUT_PULLUP); // and make SDA high i.e. send I2C STOP control.
	delayMicroseconds(10); // x. wait >5uS
	pinMode(SDA, INPUT); // and reset pins as tri-state inputs which is the default state on reset
	pinMode(SCL, INPUT);
	Serial.println("I2C Recovered");
	return 0; // all ok
}
//------------------------------------------------------------------------------------------------------------------------------------------

Radio Stack Firmware

Arduino
/****************************************************************************************/
/*																						*/
/*							Radiostack 125-PB | ©Kantooya 								*/
/*																						*/
/*		Author:		Sam Farah															*/
/*		Email:		sam.farah1986@gmail.com												*/
/*		Website:	http://samfarah.net													*/
/*		Version:	1.0																	*/
/*		Description:																	*/
/*				The firmware for the ATMega microcontroller on board, it controls		*/
/*				the circuit, and handles reading inputs and sends commands to FSX.		*/
/*																						*/
/****************************************************************************************/

//-------------------------- Headers ----------------------------
#include <AS1115.h>
#include "WSWire.h"
#include "math.h"
#include "Quadrature.h" //for rotary encoders
#include <MemoryFree.h>
#include <avr/pgmspace.h>
//---------------------------------------------------------------

//---------------------------- Pins -----------------------------
//			Name			Arduino Pin				Pin on ATMega
//		-----------			-----------				-------------
#define LED1_DS					2			//			PD2
#define LED1_CLK				3			//			PD3
#define LED1_LTCH				4			//			PD4
#define LED2_DS					5			//			PD5
#define LED2_CLK				6			//			PD6
#define LED2_LTCH				7			//			PD7
#define FrqEnc_A				8			//
#define FrqEnc_B				9			//
#define IRQ1					11			//			 
#define IRQ2					12			//			 
#define IRQ3					13			//			 
#define SDA						A4			//
#define SCL						A5			//
//---------------------------------------------------------------

//------------------------- Settings ----------------------------
#define SerialTimeout			1000
#define TempDelay				3000
#define TWI_FREQ 				50L
//---------------------------------------------------------------

//-------------------------- Enums ------------------------------
enum EDispID{ EActiveDisp = 0, EStdByDisp = 1, EATCDisp = 2 };
enum EChannel{ COM1 = 0, COM2 = 1, NAV1 = 2, NAV2 = 3, ADF = 4 };
//---------------------------------------------------------------

//----------------------- Prototypes ----------------------------
class SDisplayData;
class TDisplay;
class TEncoderData;
class TSwitchCtrl;
class TChannel;
//---------------------------------------------------------------

//------------------------- Objects -----------------------------
AS1115 DispCtrl1 = AS1115(0x01), DispCtrl2 = AS1115(0x02);
AS1115 *SwitchCtr1 = new AS1115(0x03), *SwitchCtr2 = &DispCtrl2, *SwitchCtr3 = new AS1115(0x00);
TEncoderData *FreqCtrl;
TChannel *Channel[5];
//---------------------------------------------------------------

//---------------------- Public Variables -----------------------
String LEDString = "";
TDisplay *Displays[3];
TSwitchCtrl *SwitchCtrs[3];
byte tempDigitIndex, CodeIn;
unsigned long TimeOutCheck;
bool MHz = true, MasterPower = false;
uint8_t SelectedMode;
bool ATCMode;
char temp;
short mul;
//---------------------------------------------------------------

//-------------------------- Classes ----------------------------
class TChannel
{
public:
	char MHzIncCommand[4];
	char MHzDecCommand[4];
	char KHzIncCommand[4];
	char KHzDecCommand[4];
	char SwapCommand[4];
	char CallCommand[4];
	String Active;
	String  Standby;
	uint8_t Status;
	uint8_t LEDIndex;

	TChannel(char MHzUp[], char MHzDown[], char KHzUp[], char KHzDown[], char Swap[], char call[], uint8_t LED, uint8_t StatLED)
	{
		strcpy(MHzIncCommand, strcat(MHzUp, "\0"));
		strcpy(MHzDecCommand, strcat(MHzDown, "\0"));
		strcpy(KHzIncCommand, strcat(KHzUp, "\0"));
		strcpy(KHzDecCommand, strcat(KHzDown, "\0"));
		strcpy(SwapCommand, strcat(Swap, "\0"));
		strcpy(CallCommand, strcat(call, "\0"));

		Status = StatLED;
		LEDIndex = LED;
	}
};
//---------------------------------------------------------------
class TDisplay
{
private:
	byte *Index;
	byte DigitCount;
	EDispID ID;
	bool TempValChange;
	unsigned long  LastTempChange;

	void SendValues(byte index, char value, bool ShowDP)
	{
		AS1115 *CurrentDriver = index > 7 ? &DispCtrl2 : &DispCtrl1;
		index %= 8;
		if (CurrentDriver->digitWrite(index, value, ShowDP))
		{
			//	Serial.println("I2C Froze (Displays)");
			I2C_ClearBus();
			Wire.begin();
		}
	}
public:
	String Data, TempData;
	TDisplay(byte *_Index, EDispID _ID, byte _DigitCount)
	{
		Index = _Index;
		DigitCount = _DigitCount;
		ID = _ID;
		UpdateDisplay("     ");
		TempValChange = false;
		TempData = "";
	}
	void CheckTempDisplay()
	{
		if (TempValChange && millis() - LastTempChange > TempDelay)
		{
			TempValChange = false;
			UpdateDisplay();
			tempDigitIndex = 0;
		}
	}
	void UpdateDisplay(){ UpdateDisplay(TempValChange ? TempData : Data); }
	void UpdateDisplay(String Val)
	{
		uint8_t DPFound = 0;
		for (byte i = 0; i < DigitCount + DPFound; i++)
		{
			if (Val[i] == '.')
			{
				SendValues(Index[i - 1], Val[i - 1], true);
				DPFound++;
			}
			else SendValues(Index[i - DPFound], Val[i], false);
		}
	}
	void ClearDisplay(){ UpdateDisplay("      "); }
	EDispID GetID(){ return ID; }
	void SetTempValChange(bool val)
	{
		TempValChange = val;
		LastTempChange = millis();
	}
};
//--------------------------------------------
class TEncoderData
{
private:
	int R;// a variable
	int Rold;// the old reading
	int Rdif;// the difference since last loop
	Quadrature *Encoder;
	TDisplay *Disp;

public:
	TEncoderData(uint8_t _PinA, uint8_t _PinB, TDisplay *_Disp)
	{
		pinMode(_PinA, INPUT);
		pinMode(_PinB, INPUT);
		Encoder = new Quadrature(_PinA, _PinB);
		Disp = _Disp;
	}
	void ReadEncoder(String IncCommand, String DecCommand)
	{
		R = (Encoder->position() / 2); //The /2 is to suit the encoder
		if (R != Rold) { // checks to see if it different
			(Rdif = (R - Rold));// finds out the difference
			if (Rdif == 1) Serial.println(IncCommand);
			if (Rdif == -1)Serial.println(DecCommand);
			Rold = R; // overwrites the old reading with the new one.
		}
	}
	void ReadEncoder()
	{
		R = (Encoder->position() / 2); //The /2 is to suit the encoder
		if (R != Rold) { // checks to see if it different
			(Rdif = (R - Rold));// finds out the difference
			if (Rdif == 1)
			{
				switch (mul)
				{
				case 0:Serial.println("A28"); break;
				case 1:Serial.println("A27"); break;
				case 2:Serial.println("A26"); break;
				case 3:Serial.println("A25"); break;
				}
			}
			if (Rdif == -1)
			{
				switch (mul)
				{
				case 0:Serial.println("A32"); break;
				case 1:Serial.println("A31"); break;
				case 2:Serial.println("A30"); break;
				case 3:Serial.println("A29"); break;
				}
			}
			Rold = R; // overwrites the old reading with the new one.
		}
	}
};
//--------------------------------------------
class TSwitchCtrl
{
private:
	AS1115 *Chip;
	String Data;
	String OldData;
public:
	byte IRQ;
	byte LSB, MSB;
	TSwitchCtrl(uint8_t _IRQ, AS1115 *_Chip, uint8_t _LSB, uint8_t _MSB)
	{
		IRQ = _IRQ;
		Chip = _Chip;
		LSB = _LSB;
		MSB = _MSB;
		Data = "";
		Chip->begin();
	}
	void ReadSwitchs()
	{
		uint8_t ErrorCode;
		OldData = Data;
		Data = GetLeadingZeros(Chip->ReadKeysMul(&ErrorCode), 16);//potentially not needed (the Leading Zeros, already handled from library)
		if (ErrorCode >= 4)
		{
			I2C_ClearBus();
			Wire.begin();
		}
	}
	String GetData(){ return Data; }
	String GetOldData(){ return OldData; }
};
//--------------------------------------------
void pciSetup(byte pin)
{
	*digitalPinToPCMSK(pin) |= bit(digitalPinToPCMSKbit(pin));  // enable pin
	PCIFR |= bit(digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
	PCICR |= bit(digitalPinToPCICRbit(pin)); // enable interrupt for the group
}
//----------------------- Interrupt ----------------------------
ISR(PCINT0_vect) // handle pin change interrupt for D8 to D13 here
{
	interrupts();
	for (byte SwtchCtrlIndex = 0; SwtchCtrlIndex < 3; SwtchCtrlIndex++)
		if (digitalRead(SwitchCtrs[SwtchCtrlIndex]->IRQ) == LOW)
		{
			SwitchCtrs[SwtchCtrlIndex]->ReadSwitchs();
			byte ChangedIndex = 0;
			char NewValue, InverseValue;
			for (byte i = 0; i < 16; i++)
				if (SwitchCtrs[SwtchCtrlIndex]->GetData()[i] != SwitchCtrs[SwtchCtrlIndex]->GetOldData()[i])
				{
					NewValue = SwitchCtrs[SwtchCtrlIndex]->GetData()[i];
					InverseValue = NewValue == '0' ? '1' : '0';
					//Serial.println(String(48 - i - SwitchCtrs[SwtchCtrlIndex]->LSB) + " to " + NewValue);
					ChangedIndex = 48 - i - SwitchCtrs[SwtchCtrlIndex]->LSB;
					if (ChangedIndex == 33 || ChangedIndex == 34)break;//priortize Pitot heat, due to HW design error
				}
			if (!MasterPower && ChangedIndex != 20) return;//if powered off, only listen to power swtich.
			if (NewValue == '1') //push buttons
			{
				switch (ChangedIndex)
				{
				case 33://Pitot Heat
				case 34:
					if (LEDString[28] == '0')Serial.println("C05");
					else Serial.println("C06");
					break;
				case 8:ChangeMode(COM1);			break;														//COM1 Mode
				case 16:ChangeMode(COM2);			break;														//COM2 Mode
				case 18:ChangeMode(NAV1);			break;														//NAV1 Mode
				case 23:ChangeMode(NAV2);			break;														//NAV2 Mode
				case 22:ChangeMode(ADF); mul = 0;	break;														//ADF Mode
				case 19:																						//Range Change
					if (SelectedMode == ADF)
					{
						mul += 1;
						mul %= 4;
					}
					else
					{
						MHz = !MHz;
						LEDString[9] = LEDString[9] == '1' ? '0' : '1';
						LEDString[8] = LEDString[9] == '1' ? '0' : '1';

					}
					SendLEDData(LEDString);
					break;
				case 40:Serial.println(Channel[SelectedMode]->CallCommand); break;						//Call
				case 1:																					//ATC Mode
					ATCMode = !ATCMode;
					if (ATCMode)Displays[EATCDisp]->UpdateDisplay("----");
					else Displays[EATCDisp]->UpdateDisplay();
					break;
				case 7: KeypadPress("Y131", '1'); break;													//Keypad 1
				case 15:KeypadPress("Y141", '2'); break;													//Keypad 2
				case 6: KeypadPress("Y151", '3'); break;													//Keypad 3
				case 14:KeypadPress("Y181", '4'); break;													//Keypad 4
				case 5: KeypadPress("Y191", '5'); break;													//Keypad 5
				case 13:KeypadPress("Y201", '6'); break;													//Keypad 6
				case 4: KeypadPress("Y211", '7'); break;													//Keypad 7
				case 12:KeypadPress("Y241", '0'); break;													//Keypad 0
				case 3:UpdateTempDigits('8'); break;	//potenial problem									//Keypad CLR
				case 11:																				//Keypad [8]
					if (ATCMode)Serial.println("Y221");
					else
					{
						char c[5];
						sprintf(c, "%04d", freeMemory());
						Displays[EATCDisp]->TempData = String(c);
						Displays[EATCDisp]->SetTempValChange(true);
						Displays[EATCDisp]->UpdateDisplay();
					};
					break;
				case 2:	if (ATCMode)Serial.println("Y231");												//Keypad [9]	
						else Serial.println("Y131");
						break;
				}
			}
			switch (ChangedIndex) //toggle switches
			{
			case 20:																					//Master Power

				if (NewValue == '1')PowerUp();
				else Shutdown();

				break;
			case 36:Serial.print("C43"); Serial.println(InverseValue); break;
			case 44:Serial.print("C44"); Serial.println(InverseValue); break;
			case 35:
				Serial.print("C49"); Serial.println(InverseValue); 
				Serial.print("C48"); Serial.println(InverseValue);
				break;
			case 43:Serial.print("C45"); Serial.println(InverseValue); break;
			case 37:
				Serial.print("C47"); Serial.println(InverseValue); 
				Serial.print("C41");  Serial.println(InverseValue);
				break;
			case 45:Serial.print("C42"); Serial.println(InverseValue); break;
			case 38:Serial.print("C46"); Serial.println(InverseValue); break;
			case 46:
				Serial.print("C50");  Serial.println(InverseValue);
				
				break;
			case 39:Serial.println("Y171"); break;
			case 47:Serial.println("Y161"); break;
			case 48:Serial.println("F12"); break;
			case 21:Serial.println(Channel[SelectedMode]->SwapCommand);
				MHz = true;
				LEDString[9] = '1';
				LEDString[8] = '0';
				SendLEDData(LEDString);
				break;
			}
			break;//potenial problem
		}
}

//******************************************************************************************************************************************
void setup() {
	Serial.begin(115200);
	for (int i = 0; i < 32; i++)LEDString += "0";
	ATCMode = false;
	//PinModes
	pinMode(IRQ1, INPUT);
	pinMode(IRQ2, INPUT);
	pinMode(IRQ3, INPUT);
	pinMode(LED1_DS, OUTPUT);
	pinMode(LED1_CLK, OUTPUT);
	pinMode(LED1_LTCH, OUTPUT);
	pinMode(LED2_DS, OUTPUT);
	pinMode(LED2_CLK, OUTPUT);
	pinMode(LED2_LTCH, OUTPUT);
	//Display Controlers Settings
	DispCtrl1.setFont(FONT_CODEB);
	DispCtrl2.setFont(FONT_CODEB);

	DispCtrl1.begin();

	//Switch Controlers
	SwitchCtrs[0] = new TSwitchCtrl(IRQ1, SwitchCtr1, 32, 47);
	SwitchCtrs[1] = new TSwitchCtrl(IRQ2, SwitchCtr2, 16, 31);
	SwitchCtrs[2] = new TSwitchCtrl(IRQ3, SwitchCtr3, 0, 15);

	DispCtrl1.setIntensity(0x06);
	DispCtrl2.setIntensity(0x06);
	//Display definitions
	Displays[EActiveDisp] = new TDisplay(new byte[5]{0, 1, 2, 3, 4}, EActiveDisp, 5);
	Displays[EStdByDisp] = new TDisplay(new byte[5]{5, 6, 7, 8, 9}, EStdByDisp, 5);
	Displays[EATCDisp] = new TDisplay(new byte[4]{13, 12, 11, 10}, EATCDisp, 4);
	//Rotary Encoder Definition
	FreqCtrl = new TEncoderData(FrqEnc_A, FrqEnc_B, Displays[EStdByDisp]);
	//Modes / Channels
	Channel[0] = new TChannel("A02", "A01", "A04", "A03", "A06", "A45", 7, 3);
	Channel[1] = new TChannel("A08", "A07", "A10", "A09", "A12", "A46", 6, 2);
	Channel[2] = new TChannel("A14", "A13", "A16", "A15", "A18", "A48", 5, 1);
	Channel[3] = new TChannel("A20", "A19", "A22", "A21", "A24", "A49", 4, 0);
	Channel[4] = new TChannel("A26", "A30", "A27", "A31", "", "A52", 12, 13);

	delay(50);

	for (byte i = 0; i < 3; i++)SwitchCtrs[i]->ReadSwitchs();//initialize switches - TODO: apply changes to sim accordingly
	SendLEDData(LEDString);//turn off all LEDS
	//Setup interrupt pins
	pciSetup(IRQ1);
	pciSetup(IRQ2);
	pciSetup(IRQ3);

	ChangeMode(SelectedMode);
	if (SwitchCtrs[1]->GetData()[12] == '0')Shutdown();
	else PowerUp();


}
//------------------------------------------------------------------------------------------------------------------------------------------
void loop() {
	ENCODER();  //Check the Rotary Encoders
	Displays[EATCDisp]->CheckTempDisplay();//Check if ATC display showing tmep data.
	if (!Serial.available())return;//Check if anything there
	switch (getChar())  //Get a serial read if there is.
	{
	case '=':EQUALS(); break;
	case '<':LESSTHAN(); break;
	default:while (Serial.available())Serial.read(); break;
	}
	UpdateAllDisplays();	//only if data was available.
}
//******************************************************************************************************************************************

//------------------------------------------------------------------------------------------------------------------------------------------
String GetLeadingZeros(String Bin, short digits)
{
	String Padding = "";
	for (int i = 0; i < digits - Bin.length(); i++)Padding += "0";
	return Padding + Bin;
}
//------------------------------------------------------------------------------------------------------------------------------------------
void UpdateAllDisplays()
{
	Displays[EActiveDisp]->Data = Channel[SelectedMode]->Active;
	Displays[EStdByDisp]->Data = Channel[SelectedMode]->Standby;
	if (MasterPower){
		for (int i = 0; i < 3; i++){
			if (i == 2 && ATCMode)continue;//to skip updating SQWAK display if in ATC Mode
			Displays[i]->UpdateDisplay();
		}
		SendLEDData(LEDString);
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void ChangeMode(uint8_t  Mode)
{
	SelectedMode = Mode;
	for (uint8_t i = 0; i < 5; i++)LEDString[Channel[i]->LEDIndex] = i == Mode ? '1' : '0';
	LEDString[11] = LEDString[Channel[SelectedMode]->Status];
	UpdateAllDisplays();
}
//------------------------------------------------------------------------------------------------------------------------------------------
void UpdateTempDigits(char val)
{
	if (val == '8')Displays[EATCDisp]->TempData = "1200";
	else
	{
		if (tempDigitIndex == 0)Displays[EATCDisp]->TempData = "    ";
		Displays[EATCDisp]->TempData[tempDigitIndex] = val;
		tempDigitIndex++;
		Displays[EATCDisp]->SetTempValChange(true);
		Displays[EATCDisp]->UpdateDisplay();
	}
	if (tempDigitIndex > 3 || val == '8')
	{
		Serial.println("A42" + Displays[EATCDisp]->TempData);
		Displays[EATCDisp]->SetTempValChange(false);
		if (tempDigitIndex > 0 && tempDigitIndex < 4)
		{
			Displays[EATCDisp]->Data = "1200";
			Displays[EATCDisp]->UpdateDisplay();
		}
		tempDigitIndex = 0;
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void KeypadPress(String ATCCommand, char KeypadValue)
{
	if (ATCMode)Serial.println(ATCCommand);
	else UpdateTempDigits(KeypadValue);
}
//------------------------------------------------------------------------------------------------------------------------------------------
void Shutdown()
{
	LEDString[10] = '0';
	SendLEDData("0000000000000000");
	for (byte i = 0; i < 3; i++)Displays[i]->ClearDisplay();
	MasterPower = false;
	Serial.println("A430");
}
void PowerUp()
{
	LEDString[10] = '1';
	LEDString[9] = MHz ? '1' : '0';
	LEDString[8] = LEDString[9] == '1' ? '0' : '1';
	SendLEDData(LEDString);
	for (byte i = 0; i < 3; i++)Displays[i]->UpdateDisplay();
	MasterPower = true;
	Serial.println("A431");
}
void TestLEDs(int D)
{
	String temp = "";
	for (int j = 0; j < 32; j++)temp += (random(0, 2) == 1 ? "1" : "0");
	SendLEDData(temp);
	delay(D);
}
//------------------------------------------------------------------------------------------------------------------------------------------
int ReadSwitches()
{
	uint8_t ErrorCode;
	int val = SwitchCtr1->ReadKeys(&ErrorCode);
	if (ErrorCode >= 4)
	{
		I2C_ClearBus();
		Wire.begin();
	}
	if (val > 0 && val < 255)return val;
	return 0;
}
//------------------------------------------------------------------------------------------------------------------------------------------
char getChar()// Get a character from the serial buffer
{
	TimeOutCheck = millis();
	while (Serial.available() == 0)if (millis() - TimeOutCheck > SerialTimeout)return '0';//time out command - prevent freezing
	return((char)Serial.read());
}
//------------------------------------------------------------------------------------------------------------------------------------------
void EQUALS(){
	CodeIn = getChar();
	switch (CodeIn) {
	case 'M':LEDString[3] = getChar();												break;	//COM1 Active
	case 'N':LEDString[2] = getChar();												break;	//COM2 Active
	case 'P':LEDString[1] = getChar();												break;	//NAV1 Active
	case 'Q':LEDString[0] = getChar();												break;	//NAV2 Active
	case 'S':LEDString[13] = getChar();												break;	//ADF Active
	case 'A':Channel[COM1]->Active = GetBufferValue(7);								break;	//COM1 Active
	case 'B':Channel[COM1]->Standby = GetBufferValue(7);								break;	//COM1 Stby	
	case 'C':Channel[COM2]->Active = GetBufferValue(7);								break;	//COM2 Active
	case 'D':Channel[COM2]->Standby = GetBufferValue(7);								break;	//COM2 Stby
	case 'E':Channel[NAV1]->Active = GetBufferValue(6);								break;	//NAV1 Active
	case 'F':Channel[NAV1]->Standby = GetBufferValue(6);								break;	//NAV1 Stby
	case 'G':Channel[NAV2]->Active = GetBufferValue(6);								break;	//NAV2 Active
	case 'H':Channel[NAV2]->Standby = GetBufferValue(6);								break;	//NAV2 Stby
	case 'I':Channel[ADF]->Standby = GetBufferValue(6); Channel[4]->Active = "     ";	break;	//ADF
	case 'J':Displays[EATCDisp]->Data = GetBufferValue(4);							break;	//SQUAK Set	
	}
	LEDString[11] = LEDString[Channel[SelectedMode]->Status];//update Call Stats LED
}
//------------------------------------------------------------------------------------------------------------------------------------------
void LESSTHAN()
{
	CodeIn = getChar();
	switch (CodeIn)
	{
	case 'd':	LEDString[26] = getChar();		break;
	case 'e':	LEDString[27] = getChar();		break;
	case 'b':	LEDString[28] = getChar();		break;
	case 'c':	LEDString[29] = getChar();		break;
	case 'g':											//Avionics Power	
		temp = getChar();
		if (temp == '0' && MasterPower)Serial.println("A431");
		else if (temp == '1' && !MasterPower)Serial.println("A430");//force sim to follow phyical switch
		break;
	case 'f'://LED Status
		String LightState = GetBufferValue(10);
		LEDString[18] = LightState[2];
		LEDString[19] = LightState[3];
		LEDString[20] = LightState[8];
		LEDString[21] = LightState[4];
		LEDString[22] = LightState[6];
		LEDString[23] = LightState[1];
		LEDString[17] = LightState[5];
		LEDString[16] = LightState[9];
		break;
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void ENCODER(){

	if (SelectedMode == ADF)
	{
		FreqCtrl->ReadEncoder();

	}
	else
	{
		if (MHz)FreqCtrl->ReadEncoder(Channel[SelectedMode]->MHzIncCommand, Channel[SelectedMode]->MHzDecCommand);
		else FreqCtrl->ReadEncoder(Channel[SelectedMode]->KHzIncCommand, Channel[SelectedMode]->KHzDecCommand);
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
String GetBufferValue(int byteCount)
{
	String buff = "";
	for (int i = 0; i < byteCount; i++)
	{
		temp = getChar();
		if ((temp <= '9' && temp >= '0') || temp == '.')buff += temp;
		else buff += '0';
	}
	return buff;
}
//------------------------------------------------------------------------------------------------------------------------------------------
uint8_t I2C_ClearBus() {
#if defined(TWCR) && defined(TWEN)
	TWCR &= ~(_BV(TWEN)); //Disable the Atmel 2-Wire interface so we can control the SDA and SCL pins directly
#endif

	pinMode(SDA, INPUT_PULLUP); // Make SDA (data) and SCL (clock) pins Inputs with pullup.
	pinMode(SCL, INPUT_PULLUP);
	delay(20);

	boolean SCL_LOW = (digitalRead(SCL) == LOW); // Check is SCL is Low.
	if (SCL_LOW) { //If it is held low Arduno cannot become the I2C master.
		return 1; //I2C bus error. Could not clear SCL clock line held low
	}

	boolean SDA_LOW = (digitalRead(SDA) == LOW);  // vi. Check SDA input.
	int clockCount = 20; // > 2x9 clock

	while (SDA_LOW && (clockCount > 0)) { //  vii. If SDA is Low,
		clockCount--;
		// Note: I2C bus is open collector so do NOT drive SCL or SDA high.
		pinMode(SCL, INPUT); // release SCL pullup so that when made output it will be LOW
		pinMode(SCL, OUTPUT); // then clock SCL Low
		delayMicroseconds(10); //  for >5uS
		pinMode(SCL, INPUT); // release SCL LOW
		pinMode(SCL, INPUT_PULLUP); // turn on pullup resistors again
		// do not force high as slave may be holding it low for clock stretching.
		delayMicroseconds(10); //  for >5uS
		// The >5uS is so that even the slowest I2C devices are handled.
		SCL_LOW = (digitalRead(SCL) == LOW); // Check if SCL is Low.
		int counter = 20;
		while (SCL_LOW && (counter > 0)) {  //  loop waiting for SCL to become High only wait 2sec.
			counter--;
			delay(50);
			SCL_LOW = (digitalRead(SCL) == LOW);
		}
		if (SCL_LOW) { // still low after 2 sec error
			return 2; // I2C bus error. Could not clear. SCL clock line held low by slave clock stretch for >2sec
		}
		SDA_LOW = (digitalRead(SDA) == LOW); //   and check SDA input again and loop
	}
	if (SDA_LOW) { // still low
		return 3; // I2C bus error. Could not clear. SDA data line held low
	}

	// else pull SDA line low for Start or Repeated Start
	pinMode(SDA, INPUT); // remove pullup.
	pinMode(SDA, OUTPUT);  // and then make it LOW i.e. send an I2C Start or Repeated start control.
	// When there is only one I2C master a Start or Repeat Start has the same function as a Stop and clears the bus.
	// A Repeat Start is a Start occurring after a Start with no intervening Stop.
	delayMicroseconds(10); // wait >5uS
	pinMode(SDA, INPUT); // remove output low
	pinMode(SDA, INPUT_PULLUP); // and make SDA high i.e. send I2C STOP control.
	delayMicroseconds(10); // x. wait >5uS
	pinMode(SDA, INPUT); // and reset pins as tri-state inputs which is the default state on reset
	pinMode(SCL, INPUT);
	//Serial.println("I2C Recovered");
	return 0; // all ok
}
//------------------------------------------------------------------------------------------------------------------------------------------
void PulseLEN(uint8_t pin, bool StartLow)
{
	if (StartLow)
	{
		digitalWrite(pin, LOW);
		digitalWrite(pin, HIGH); //fix for double send
		digitalWrite(pin, LOW);
	}
	else
	{
		digitalWrite(pin, HIGH);
		digitalWrite(pin, LOW);
		digitalWrite(pin, HIGH);
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void PulseData(uint8_t DSpin, uint8_t CLKPin, char Data)
{
	digitalWrite(DSpin, (Data == '1' ? HIGH : LOW));
	digitalWrite(CLKPin, LOW);
	digitalWrite(CLKPin, HIGH);
}
//------------------------------------------------------------------------------------------------------------------------------------------
void SendLEDData(String data)
{
	PulseLEN(LED1_LTCH, false);
	for (int i = 15; i >= 0; i--)PulseData(LED1_DS, LED1_CLK, data[i]);
	PulseLEN(LED1_LTCH, true);

	PulseLEN(LED2_LTCH, false);
	for (int i = 31; i >= 16; i--)PulseData(LED2_DS, LED2_CLK, data[i]);
	PulseLEN(LED2_LTCH, true);
}
//------------------------------------------------------------------------------------------------------------------------------------------

Electrical Control Firmare

Arduino
/****************************************************************************************/
/*																						*/
/*							Electrical Control  | ©Kantooya								*/
/*																						*/
/*		Author:		Sam Farah															*/
/*		Email:		sam.farah1986@gmail.com												*/
/*		Website:	http://samfarah.net													*/
/*		Version:	1.0																	*/
/*		Description:																	*/
/*				The firmware for the ATMega microcontroller on board, it controls		*/
/*				the circuit, and handles reading inputs and sends commands to FSX.		*/
/*																						*/
/****************************************************************************************/


//-------------------------- Headers ----------------------------
#include <U8g2lib.h>
#include <AS1115.h>
//#include <Arduino.h>
#include <Quadrature.h> //for rotary encoders
//#ifdef U8X8_HAVE_HW_SPI
//#include <SPI.h>
//#endif
#ifdef U8X8_HAVE_HW_I2C
#include <WSWire.h>
#endif
#include <MemoryFree.h>
#include <avr/pgmspace.h>
//---------------------------------------------------------------

//---------------------------- Pins -----------------------------
//			Name			Arduino Pin				Pin on ATMega
//		-----------			-----------				-------------
#define LED2_CLK				PD5
#define LED2_DS					PD7
#define LED2_LTCH				PD6
#define LED1_CLK				PD3
#define LED1_DS					PD2
#define LED1_LTCH			    PD4
#define	IRQ1					A0
#define	IRQ2					A1
//---------------------------------------------------------------


//------------------------- Settings ----------------------------
#define TCAADDR					0x70
#define SerialTimeout			1000
#define TempDelay				3000
#define EBaro					0
#define ENSet					1
//---------------------------------------------------------------

//----------------------- Prototypes ----------------------------
class TDisplay;
class TEncoderData;
class TSwitchCtrl;
class TChannel;
class TPowerData;
class TInstrumentData;
//---------------------------------------------------------------

//------------------------- Objects -----------------------------
AS1115 disp = AS1115(0x00);
AS1115 *SwitchCtr1 = new AS1115(0x00), *SwitchCtr2 = new AS1115(0x00);
TSwitchCtrl *SwitchCtrs[2];
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

Quadrature Encoder1(8, 9);
Quadrature Encoder2(A2, A3);
//---------------------------------------------------------------

//-------------------------- Enums ------------------------------
//enum EDispID { EBaro = 0, ENSet = 1 };
//---------------------------------------------------------------

//---------------------- Public Variables -----------------------
//unsigned long TimeOutCheck;
//byte counter = 0;
byte tempDigitIndex, CodeIn;
String LED1String = "";
String LED2String = "";

char temp;
TDisplay *Displays[2];
TPowerData *PowerData;
TInstrumentData *InstrumentData;
bool MasterPower = true;
bool UpdatePowerDisp = false;
bool UpdateInstrumentDisp = false;
static const byte TickPositions[] PROGMEM = { 39,41,43,50,60,71,92,	102, 124 };
static const byte GraphOrder[] PROGMEM = { 8,9,10,11,12,13,14,15,0,1 };
//static const byte BaroDigits[]  = { 3,4,5,6 };
//static const byte NSetDigits[]  = { 0, 1, 2 };
bool SpeedSet = false;
bool N1Set = false;
bool Strtr1 = false;
bool Strtr2 = false;
uint8_t Autobreak = 0;
uint8_t LastAddress = 7;
int8_t R1old, R2old, R1dif, R2dif;
//---------------------------------------------------------------
class TDisplay
{
private:
	byte *Index;
	byte DigitCount;
	//byte ID;
	//bool TempValChange;
	bool ShowADP;
	//unsigned long  LastTempChange;

	void SendValues(byte index, char value, bool ShowDP)
	{
		//return;
		tcaselect(3, true);
		AS1115 *CurrentDriver = &disp;// index > 7 ? &DispCtrl2 : &DispCtrl1;
		index %= 8;
		if (CurrentDriver->digitWrite(index, value, ShowDP))
		{
			//	Serial.println("I2C Froze (Displays)");
			I2C_ClearBus();
			Wire.begin();
		}
	}
public:
	String Data;// , TempData;
	TDisplay(byte *_Index, byte _DigitCount, bool _showADP)
	{
		Data.reserve(4);
		//TempData.reserve(4);
		ShowADP = _showADP;
		Index = _Index;
		DigitCount = _DigitCount;
		//ID = _ID;
		UpdateDisplay("     ");
		//TempValChange = false;
		//TempData = "";
	}
	void UpdateDisplay() { UpdateDisplay(Data); }
	void UpdateDisplay(String Val)
	{
		uint8_t DPFound = 0;
		for (byte i = 0; i < DigitCount + DPFound; i++)
		{
			if (Val[i] == '.')
			{
				SendValues(Index[i - 1], Val[i - 1], ShowADP);
				DPFound++;
			}
			else SendValues(Index[i - DPFound], Val[i], false);
		}
	}
	void ClearDisplay() { UpdateDisplay("      "); }
	//	byte GetID() { return ID; }
		//void SetTempValChange(bool val)
		//{
		//	TempValChange = val;
		//	//	LastTempChange = millis();
		//}
	byte GetDigitCount() { return DigitCount; }
};
//--------------------------------------------
class TPowerData
{
public:
	String BatteryVoltage;
	String BatteryCurrent;
	String MainBusVoltage;
	String MainBusCurrent;
	String APUVoltage;
	bool APUGenActive;
	TPowerData()
	{
		BatteryVoltage.reserve(4);
		BatteryCurrent.reserve(4);
		MainBusVoltage.reserve(4);
		MainBusCurrent.reserve(3);
		APUVoltage.reserve(2);
	}
};
//--------------------------------------------
class TInstrumentData
{
public:
	uint8_t Flaps;
	uint8_t FlapsHandle;
	uint8_t FlapsPoisitonCount;
	uint8_t FuelLeft;
	uint8_t FuelCentre;
	uint8_t FuelRight;
	String Autobreak;
	TInstrumentData() { Autobreak.reserve(3); }
	void SetFlaps(String StrFlaps) { Flaps = atoi(StrFlaps.c_str()); }
	void SetFlapsHandle(String strFlapsHandle) { FlapsHandle = atoi(strFlapsHandle.c_str()); }
	void SetFlapsPoisitonCount(String strFlapsPoisitonCount) { FlapsPoisitonCount = atoi(strFlapsPoisitonCount.c_str()); }
	void SetFuelLeft(String strFuel) { FuelLeft = atoi(strFuel.c_str()); }
	void SetFuelCentre(String strFuel) { FuelCentre = atoi(strFuel.c_str()); }
	void SetFuelRight(String strFuel) { FuelRight = atoi(strFuel.c_str()); }
	void SetAutoBreak(char Val)
	{
		switch (Val)
		{
		case '0':Autobreak = "RTO"; break;
		case '1':Autobreak = "OFF"; break;
		case '2':Autobreak = " 1 "; break;
		case '3':Autobreak = " 2 "; break;
		case '4':Autobreak = " 3 "; break;
		case '5':Autobreak = "MAX"; break;
		}
	}
};

class TSwitchCtrl
{
private:
	AS1115 *Chip;
	String Data;
	String OldData;
public:
	byte MuxAddress;
	byte IRQ;
	byte LSB, MSB;
	TSwitchCtrl(uint8_t _IRQ, AS1115 *_Chip, uint8_t _LSB, uint8_t _MSB, uint8_t _MuxAddress)
	{
		Data.reserve(16);
		OldData.reserve(16);
		IRQ = _IRQ;
		Chip = _Chip;
		LSB = _LSB;
		MSB = _MSB;
		MuxAddress = _MuxAddress;
		Data = "";
		tcaselect(MuxAddress, false);
		Chip->begin(0x00, 0x00);
		Chip->begin(0x01, 0x00);
		Chip->begin(0x02, 0x00);
		Chip->begin(0x03, 0x00);
	}
	void ReadSwitchs()
	{
		tcaselect(MuxAddress, false);
		uint8_t ErrorCode;
		//Serial.print("Saving ");
		//Serial.print(Data);
		//Serial.println(" as old");
		OldData = Data;
		Data = Chip->ReadKeysMul(&ErrorCode);//potentially not needed (the Leading Zeros, already handled from library)
		//Serial.print("new val is: ");
		//Serial.println (Data);
		if (ErrorCode >= 4)
		{
			I2C_ClearBus();
			Wire.begin();
		}
		tcaselect(LastAddress, false);
	}
	String GetData() { return Data; }
	String GetOldData() { return OldData; }
	void EmptyBuffer()
	{
		tcaselect(MuxAddress, false);
		uint8_t ErrorCode;

		Chip->ReadKeysMul(&ErrorCode);//potentially not needed (the Leading Zeros, already handled from library)
		if (ErrorCode >= 4)
		{
			I2C_ClearBus();
			Wire.begin();
		}
		tcaselect(LastAddress, false);
	}
};
/*
Frame Buffer Examples: clearBuffer/sendBuffer. Fast, but may not work with all Arduino boards because of RAM consumption
Page Buffer Examples: firstPage/nextPage. Less RAM usage, should work with all Arduino boards.
U8x8 Text Only Example: No RAM usage, direct communication with display controller. No graphics, 8x8 Text only.
*/


void tcaselect(uint8_t i, bool SaveAddress) {
	if (i > 7) return;
	if (SaveAddress == true)		LastAddress = i;
	Wire.beginTransmission(TCAADDR);
	Wire.write(1 << i);
	Wire.endTransmission();
	//delay(10);
}

void u8g2_prepare(void) {
	u8g2.setFont(u8g2_font_6x10_tf);
	u8g2.setFontRefHeightExtendedText();
	u8g2.setDrawColor(1);
	u8g2.setFontPosTop();
	u8g2.setFontDirection(0);
}
void PowerDevice()
{
	if (MasterPower == 0)
	{
		SendLED2Data("0000000000000000");
		SendLED1Data("0000000000000000");
		Displays[EBaro]->UpdateDisplay("    ");
		Displays[ENSet]->UpdateDisplay("   ");
		tcaselect(6, true);
		u8g2.clearDisplay();
		tcaselect(7, true);
		u8g2.clearDisplay();
		
	}
	else
	{
		UpdateAllDisplays();
		UpdateInstrumentDisplay();
		UpdatePowerDisplay();
	}
}
ISR(PCINT1_vect) // handle pin change interrupt for D8 to D13 here
{
	interrupts();
	for (byte SwtchCtrlIndex = 0; SwtchCtrlIndex < 2; SwtchCtrlIndex++)
	{
		if (digitalRead(SwitchCtrs[SwtchCtrlIndex]->IRQ) == LOW)
		{
			SwitchCtrs[SwtchCtrlIndex]->ReadSwitchs();
			byte ChangedIndex = 0;
			char NewValue, InverseValue;
			for (byte i = 0; i < 16; i++)
			{

				if (SwitchCtrs[SwtchCtrlIndex]->GetData()[i] != SwitchCtrs[SwtchCtrlIndex]->GetOldData()[i])
				{
					NewValue = SwitchCtrs[SwtchCtrlIndex]->GetData()[i];
					InverseValue = NewValue == '0' ? '1' : '0';
					ChangedIndex = 32 - i - SwitchCtrs[SwtchCtrlIndex]->LSB;
					break;
				}
			}
			if (!MasterPower && ChangedIndex != 31) return;//if powered off, only listen to power swtich.
			if (NewValue == '1') //push buttons
			{
				switch (ChangedIndex)
				{
				case 1:Serial.println(F("C15"));										break;
				case 2:Serial.println(F("C14"));										break;
				case 8:Serial.println(F("E43")); Serial.println(F("E46"));				break;
				case 14:Serial.println(F("C02"));										break;
				case 15:Serial.println(F("C01"));										break;
				case 22:Serial.println(F("F31"));										break;
				case 23:Serial.println(F("B31"));										break;
				case 25:Serial.println(F("B35"));										break;
				case 27:Serial.println(F("C13"));										break;
				case 28:Serial.println(LED1String[4] == '1' ? F("C21") : F("C20"));		break;
				case 29:Serial.println(F("C04"));										break;
				case 30:Serial.println(F("C16"));										break;
					//case 26:Serial.println("F34");										break; AUTO BARO??
				case 32:Serial.println(F("F34"));										break;
				}
			}
			switch (ChangedIndex) //toggle switches
			{
			case 4:Serial.println(NewValue == '1' ? F("E17") : F("E18"));	break;
			case 5:Serial.println(NewValue == '1' ? F("E32") : F("E31"));	break;
			case 6:Serial.println(NewValue == '1' ? F("E30") : F("C11"));	break;
			case 7:Serial.println(NewValue == '0' ? F("E30") : F("C11"));	break;
			case 12:Serial.println(NewValue == '1' ? F("E23") : F("E24"));	break;
			case 13:Serial.println(NewValue == '1' ? F("E20") : F("E21"));	break;
				//case 13:Serial.println(F("E21")); break;
			case 17:Autobreak = 4; SetAB();									break;
			case 18:Autobreak = 3; SetAB();									break;
			case 19:Autobreak = 2; SetAB();									break;
			case 20:Autobreak = 1; SetAB();									break;
			case 21:Autobreak = 0; SetAB();									break;
			case 24:Autobreak = 5; SetAB();									break;
			case 31:MasterPower = NewValue == '1';
				PowerDevice();
				break;
			}
		}
	}
}
void setup(void) {

	LED1String.reserve(16);
	LED2String.reserve(16);
	//BargraphStirng.reserve(10);

	Serial.begin(115200);
	pinMode(IRQ1, INPUT_PULLUP);
	pinMode(IRQ2, INPUT_PULLUP);
	pinMode(LED1_CLK, OUTPUT);
	pinMode(LED1_DS, OUTPUT);
	pinMode(LED1_LTCH, OUTPUT);
	pinMode(LED2_CLK, OUTPUT);
	pinMode(LED2_DS, OUTPUT);
	pinMode(LED2_LTCH, OUTPUT);


	SwitchCtrs[0] = new TSwitchCtrl(IRQ1, SwitchCtr1, 0, 15, 5);
	SwitchCtrs[1] = new TSwitchCtrl(IRQ2, SwitchCtr2, 16, 31, 4);

	tcaselect(3, false);
	disp.setFont(FONT_CODEB);
	disp.begin(0x03, 0x00);
	disp.setIntensity(0x06);


	Displays[EBaro] = new TDisplay(new byte[5]{ 3,4,5,6 }, 4, true);
	Displays[ENSet] = new TDisplay(new byte[5]{ 0, 1, 2 }, 3, false);

	PowerData = new TPowerData();
	InstrumentData = new TInstrumentData;

	SwitchCtrs[0]->ReadSwitchs();
	SwitchCtrs[1]->ReadSwitchs();

	MasterPower = SwitchCtrs[0]->GetData()[1] == '1';
	PowerDevice();

	delay(50);
	pciSetup(IRQ1);
	pciSetup(IRQ2);


	tcaselect(6, false);
	u8g2.begin();
	tcaselect(7, false);
	u8g2.begin();

	UpdatePowerDisp = false;
	UpdateInstrumentDisp = false;

	LED1String = "000000000000000000000000";
	LED2String = "000000000000000000000000";
	//u8g2.setFlipMode(0);
}
void loop(void) {
	ENCODER();  //Check the Rotary Encoders	
	if (!Serial.available()) //Check if anything there
	{
		if (UpdatePowerDisp == true && MasterPower == true) UpdatePowerDisplay();
		if (UpdateInstrumentDisp == true && MasterPower == true) UpdateInstrumentDisplay();
		return;
	}

	while (Serial.available())
	{
		switch (getChar())  //Get a serial read if there is.
		{
		case '=':EQUALS(); break;
		case '<':LESSTHAN();  break;
		case '?':QUESTION();  break;
		case '#':HASHTAG();  break;
		case '$':DOLLAR();  break;
		default:break; // ignore floating values //while (Serial.available())  Serial.read(); break; // empty buffer	 
		}
	}
	while (Serial.available())  Serial.read(); // empty buffer	 
	UpdateAllDisplays();
}
void pciSetup(byte pin)
{
	*digitalPinToPCMSK(pin) |= bit(digitalPinToPCMSKbit(pin));  // enable pin
	PCIFR |= bit(digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
	PCICR |= bit(digitalPinToPCICRbit(pin)); // enable interrupt for the group
}
void SetAB()
{
	for (byte i = 0; i < 6; i++)Serial.println(F("Y031"));
	for (byte i = 0; i < Autobreak; i++)Serial.println(F("Y021"));
}
//------------------------------------------------------------------------------------------------------------------------------------------
void UpdateAllDisplays()
{
	if (MasterPower) {
		//for (byte i = 0; i < 2; i++) {
		Displays[EBaro]->UpdateDisplay();
		Displays[ENSet]->UpdateDisplay();
		//}
		SendLED2Data(LED2String);
		LED1String[7] = !SpeedSet && N1Set ? '1' : '0';
		LED1String[6] = MasterPower ? '1' : '0';
		LED1String[15] = Displays[EBaro]->Data == "29.92" ? '1' : '0';
		SendLED1Data(LED1String);

	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
char getChar()// Get a character from the serial buffer
{
	//TimeOutCheck = millis();
	while (Serial.available() == 0);// if (millis() - TimeOutCheck > SerialTimeout)return '0';//time out command - prevent freezing
	return((char)Serial.read());
}
//------------------------------------------------------------------------------------------------------------------------------------------
void EQUALS() {

	CodeIn = getChar();
	switch (CodeIn) {
	case 't':	LED1String[12] = getChar();		break;					//auto throttle
	case 's':	SpeedSet = getChar() == '1' ? true : false;		break;//airspeed
	case 'u':	N1Set = getChar() == '1' ? true : false;		break;//n1set
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void QUESTION() {
	CodeIn = getChar();
	String GenStats, GearStats;
	switch (CodeIn) {
	case 'k': Displays[EBaro]->Data = GetBufferValue(5);				break;	//Kohlsman setting Hg
	case 'I': PowerData->BatteryVoltage = GetBufferValue(4); UpdatePowerDisp = true;				break;	//Battery Voltage
	case 'J': PowerData->BatteryCurrent = GetBufferValue(4); UpdatePowerDisp = true;				break;	//Battery Current
	case 'K': PowerData->MainBusVoltage = GetBufferValue(4); UpdatePowerDisp = true;				break;	//Main Bus Voltage
	case 's':
		GenStats = GetBufferValue(2);
		LED1String[10] = GenStats[0] == '1' ? '0' : '1';
		LED1String[11] = GenStats[1] == '1' ? '0' : '1';
		break;
	case 'Y'://gear
		GearStats = GetBufferValue(3);
		LED2String[2] = GearStats[2] == '1' ? '1' : '0';//right red
		LED2String[4] = GearStats[1] == '1' ? '1' : '0';//left red
		LED2String[6] = GearStats[0] == '1' ? '1' : '0';//nose red
		LED2String[3] = GearStats[2] == '2' ? '1' : '0';//right grn
		LED2String[5] = GearStats[1] == '2' ? '1' : '0';//left grn
		LED2String[7] = GearStats[0] == '2' ? '1' : '0';//nose grn
		break;
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void LESSTHAN()
{
	CodeIn = getChar();
	switch (CodeIn)
	{
	case 'G':	InstrumentData->SetFlaps(GetBufferValue(3)); UpdateInstrumentDisp = true;				break;	 //Flaps
	case 'X':	InstrumentData->SetFuelLeft(GetBufferValue(3)); UpdateInstrumentDisp = true;			break;	 //Flaps
	case 'Y':	InstrumentData->SetFuelCentre(GetBufferValue(3)); UpdateInstrumentDisp = true;			break;	 //Flaps
	case 'Z':	InstrumentData->SetFuelRight(GetBufferValue(3)); UpdateInstrumentDisp = true;			break;	 //Flaps
	case 'I':	LED1String[1] = getChar();		break;	//Plane on Ground
	case 'q':	LED1String[5] = getChar();		break;	//Parking Break
	case 'i':	LED1String[4] = getChar();		break;	//Spoiler Arked
	case 'r':	LED1String[2] = getChar();		break;	//Eng 1 Fuel Vales
	case 's':	LED1String[0] = getChar();		break;	//Eng 2 Fuel Vales
	case 'k':	Strtr1 = (getChar() == '1' ? true : false); LED1String[3] = (Strtr1 || Strtr2) ? '1' : '0';	break;	//eng starter
	case 'l':	Strtr2 = (getChar() == '1' ? true : false); LED1String[3] = (Strtr1 || Strtr2) ? '1' : '0';	break;	//eng starter

	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void HASHTAG() {
	CodeIn = getChar();
	switch (CodeIn)
	{
	case 'H':	Displays[ENSet]->Data = GetBufferValue(4);													break;
	case 'I':	PowerData->MainBusCurrent = GetBufferValue(3); UpdatePowerDisp = true;						break;	//Main Bus Current
	case 'L':	InstrumentData->SetFlapsHandle(GetBufferValue(2)); UpdateInstrumentDisp = true;				break;	//Flaps Index
	case 'K':	InstrumentData->SetFlapsPoisitonCount(GetBufferValue(2)); UpdateInstrumentDisp = true;		break;	//Flaps Index
	case 'J':	InstrumentData->SetAutoBreak(getChar()); UpdateInstrumentDisp = true;		break;	//Flaps Index
	case 'N':	LED1String[14] = getChar(); break;	//HAS G/S
	case 'M':	LED1String[13] = getChar(); break;	//HAS LOC
	}

}
//------------------------------------------------------------------------------------------------------------------------------------------

void ENCODER() {

	int8_t R1 = (Encoder1.position() / 2); //The /2 is to suit the encoder
	if (R1 != R1old) { // checks to see if it different
		(R1dif = (R1 - R1old));// finds out the difference
		//Serial.println(Encoder1.position());
		if (R1dif == 1) Serial.println(F("B43"));
		if (R1dif == -1)Serial.println(F("B44"));
		R1old = R1; // overwrites the old reading with the new one.
		//Disp->SetTempValChange(true);
	}

	int8_t R2 = (Encoder2.position() / 2); //The /2 is to suit the encoder
	if (R2 != R2old) { // checks to see if it different
		(R2dif = (R2 - R2old));// finds out the difference
		if (R2dif == 1) Serial.println(F("C25"));
		if (R2dif == -1)Serial.println(F("C26"));
		R2old = R2; // overwrites the old reading with the new one.
					//Disp->SetTempValChange(true);
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void DOLLAR() {
	CodeIn = getChar();
	switch (CodeIn)
	{
	case 'g':	GETBarLED(GetBufferValue(3));								break;
	case 'i':	PowerData->APUVoltage = GetBufferValue(2);	UpdatePowerDisp = true;				break;	//APU Voltage
	case 'k':	PowerData->APUGenActive = getChar() == '1' ? true : false; 		break;	//APU Gen Active (considers APU RPM)
	case 'j':	LED1String[9] = getChar();		break; //APU Gen
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
String GetBufferValue(int byteCount)
{
	String buff = "";
	for (byte i = 0; i < byteCount; i++)
	{
		temp = getChar();
		if ((temp <= '9' && temp >= '0') || temp == '.' || temp == '-' || temp == '+')buff += temp;
		else buff += '0';
	}
	return buff;
}
//------------------------------------------------------------------------------------------------------------------------------------------
uint8_t I2C_ClearBus() {
#if defined(TWCR) && defined(TWEN)
	TWCR &= ~(_BV(TWEN)); //Disable the Atmel 2-Wire interface so we can control the SDA and SCL pins directly
#endif

	pinMode(SDA, INPUT_PULLUP); // Make SDA (data) and SCL (clock) pins Inputs with pullup.
	pinMode(SCL, INPUT_PULLUP);
	delay(20);

	boolean SCL_LOW = (digitalRead(SCL) == LOW); // Check is SCL is Low.
	if (SCL_LOW) { //If it is held low Arduno cannot become the I2C master.
		return 1; //I2C bus error. Could not clear SCL clock line held low
	}

	boolean SDA_LOW = (digitalRead(SDA) == LOW);  // vi. Check SDA input.
	byte clockCount = 20; // > 2x9 clock

	while (SDA_LOW && (clockCount > 0)) { //  vii. If SDA is Low,
		clockCount--;
		// Note: I2C bus is open collector so do NOT drive SCL or SDA high.
		pinMode(SCL, INPUT); // release SCL pullup so that when made output it will be LOW
		pinMode(SCL, OUTPUT); // then clock SCL Low
		delayMicroseconds(10); //  for >5uS
		pinMode(SCL, INPUT); // release SCL LOW
		pinMode(SCL, INPUT_PULLUP); // turn on pullup resistors again
									// do not force high as slave may be holding it low for clock stretching.
		delayMicroseconds(10); //  for >5uS
							   // The >5uS is so that even the slowest I2C devices are handled.
		SCL_LOW = (digitalRead(SCL) == LOW); // Check if SCL is Low.
		byte counter = 20;
		while (SCL_LOW && (counter > 0)) {  //  loop waiting for SCL to become High only wait 2sec.
			counter--;
			delay(50);
			SCL_LOW = (digitalRead(SCL) == LOW);
		}
		if (SCL_LOW) { // still low after 2 sec error
			return 2; // I2C bus error. Could not clear. SCL clock line held low by slave clock stretch for >2sec
		}
		SDA_LOW = (digitalRead(SDA) == LOW); //   and check SDA input again and loop
	}
	if (SDA_LOW) { // still low
		return 3; // I2C bus error. Could not clear. SDA data line held low
	}

	// else pull SDA line low for Start or Repeated Start
	pinMode(SDA, INPUT); // remove pullup.
	pinMode(SDA, OUTPUT);  // and then make it LOW i.e. send an I2C Start or Repeated start control.
						   // When there is only one I2C master a Start or Repeat Start has the same function as a Stop and clears the bus.
						   // A Repeat Start is a Start occurring after a Start with no intervening Stop.
	delayMicroseconds(10); // wait >5uS
	pinMode(SDA, INPUT); // remove output low
	pinMode(SDA, INPUT_PULLUP); // and make SDA high i.e. send I2C STOP control.
	delayMicroseconds(10); // x. wait >5uS
	pinMode(SDA, INPUT); // and reset pins as tri-state inputs which is the default state on reset
	pinMode(SCL, INPUT);
	//Serial.println("I2C Recovered");
	return 0; // all ok
}
//------------------------------------------------------------------------------------------------------------------------------------------


void PulseLEN(uint8_t pin, bool StartLow)
{
	if (StartLow)
	{
		digitalWrite(pin, LOW);
		digitalWrite(pin, HIGH); //fix for double send
		digitalWrite(pin, LOW);
	}
	else
	{
		digitalWrite(pin, HIGH);
		digitalWrite(pin, LOW);
		digitalWrite(pin, HIGH);
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------
void PulseData(uint8_t DSpin, uint8_t CLKPin, char Data)
{
	digitalWrite(DSpin, (Data == '1' ? HIGH : LOW));
	digitalWrite(CLKPin, LOW);
	digitalWrite(CLKPin, HIGH);
}
//------------------------------------------------------------------------------------------------------------------------------------------
void SendLED1Data(String data)
{
	PulseLEN(LED1_LTCH, false);
	for (byte i = 0; i < 16; i++)PulseData(LED1_DS, LED1_CLK, data[i]);
	PulseLEN(LED1_LTCH, true);

	/*  PulseLEN(LED2_LTCH, false);
	for (int i = 31; i >= 16; i--)PulseData(LED2_DS, LED2_CLK, data[i]);
	PulseLEN(LED2_LTCH, true);*/
}
//------------------------------------------------------------------------------------------------------------------------------------------
void SendLED2Data(String data)
{
	PulseLEN(LED2_LTCH, false);
	for (byte i = 0; i < 16; i++) {
		PulseData(LED2_DS, LED2_CLK, data[i]); //delay(10);
	}
	PulseLEN(LED2_LTCH, true);

	/*  PulseLEN(LED2_LTCH, false);
	for (int i = 31; i >= 16; i--)PulseData(LED2_DS, LED2_CLK, data[i]);
	PulseLEN(LED2_LTCH, true);*/
}
//------------------------------------------------------------------------------------------------------------------------------------------
void GETBarLED(String APURPMVal)
{
	String BargraphStirng = "";
	if (APURPMVal[0] == '1')
	{
		BargraphStirng = "1111111111";
	}
	else
	{
		byte val = APURPMVal[1] - '0';
		BargraphStirng = "";
		for (byte i = 0; i < val; i++)
			BargraphStirng += '1';
		for (byte i = 0; i < 10 - val; i++)
			BargraphStirng += '0';
	}

	for (byte i = 0; i < 10; i++)
		LED2String[pgm_read_byte(&(GraphOrder[i]))] = BargraphStirng[i];
}
//------------------------------------------------------------------------------------------------------------------------------------------
void UpdatePowerDisplay()
{
	//return;
	tcaselect(6, true);
	u8g2.firstPage();
	do
	{

		u8g2.drawHLine(0, 17, 128);
		u8g2.drawHLine(0, 42, 128);
		u8g2.drawVLine(64, 0, 17);
		u8g2.drawVLine(43, 42, 22);
		u8g2.drawVLine(86, 42, 22);



		u8g2_prepare();
		u8g2.setFont(u8g2_font_freedoomr10_tu);
		u8g2.drawStr(10, 0, (PowerData->BatteryVoltage + "V").c_str());
		u8g2.drawStr(80, 0, (PowerData->BatteryCurrent + "A").c_str());
		u8g2.drawStr(3, 50, (PowerData->MainBusVoltage + "V").c_str());
		u8g2.drawStr(51, 50, (PowerData->MainBusCurrent + "A").c_str());
		u8g2.drawStr(97, 50, (PowerData->APUVoltage + "V").c_str());

		if (PowerData->BatteryCurrent[0] == '-')
		{
			u8g2.setFont(u8g2_font_crox3hb_tf);
			u8g2.drawStr(2, 23, "LOW BATTERY");
		}

		u8g2.setFont(u8g2_font_6x10_tf);

	} while (u8g2.nextPage());


	UpdatePowerDisp = false;

}
//------------------------------------------------------------------------------------------------------------------------------------------
void UpdateInstrumentDisplay()
{
	//return;
	uint8_t i;
	uint8_t CentrePos;
	uint8_t actualpos;
	tcaselect(7, true);
	u8g2.firstPage();
	u8g2_prepare();
	do
	{
		u8g2.drawVLine(34, 0, 44);
		u8g2.drawHLine(0, 44, 128);
		u8g2.drawVLine(42, 45, 21);
		u8g2.drawVLine(86, 45, 21);


		// Fuel 
		u8g2.setFont(u8g2_font_4x6_tf);
		u8g2.drawStr(13, 47, "LEFT");
		u8g2.drawStr(55, 47, "CENTRE");
		u8g2.drawStr(100, 47, "RIGHT");
		u8g2.drawStr(40, 2, "FLAPS:");
		u8g2.setFont(u8g2_font_6x10_tf);

		u8g2.drawStr(13, 56, (String(InstrumentData->FuelLeft) + "%").c_str());
		u8g2.drawStr(55, 56, (String(InstrumentData->FuelCentre) + "%").c_str());
		u8g2.drawStr(100, 56, (String(InstrumentData->FuelRight) + "%").c_str());

		//	//Autobreak
		u8g2.drawStr(8, 15, InstrumentData->Autobreak.c_str());

		//	//Flaps Demo
		u8g2.setFont(u8g2_font_micro_mn);


		u8g2.drawStr(42, 35, "0");//save ram
		u8g2.drawStr(40, 35, "1");
		//u8g2.drawStr(44, 35, "2");
		u8g2.drawStr(48, 35, "5");
		u8g2.drawStr(56, 35, "10");
		u8g2.drawStr(67, 35, "15");
		u8g2.drawStr(88, 35, "25");
		u8g2.drawStr(98, 35, "30");
		u8g2.drawStr(120, 35, "40");//save ram ends

			//u8g2.setFont(u8g2_font_4x6_tf);

		//u8g2.setFont(u8g2_font_6x10_tf);
		u8g2.drawHLine(44, 30, 81);

		for (i = 0; i < 9; i++)u8g2.drawVLine(pgm_read_byte(&(TickPositions[i])), 29, 3); //SAVE RAM


		actualpos = map(InstrumentData->Flaps, 0, 100, 0, 85);
		//u8g2.drawStr(50, 5, String(39 + actualpos).c_str());



		u8g2.drawVLine(39, 20, 7);
		u8g2.drawBox(39, 20, actualpos, 7);
		if (InstrumentData->FlapsPoisitonCount == 8)
		{
			CentrePos = pgm_read_byte(&(TickPositions[InstrumentData->FlapsHandle]));
			u8g2.drawVLine(CentrePos, 10, 4);
			u8g2.drawTriangle(CentrePos - 2, 14, CentrePos, 18, CentrePos + 3, 14); //tales waaayyy tooo much RAM
		}
		//	//u8g2.drawVLine(i , 20, 8);

	} while (u8g2.nextPage());
	UpdateInstrumentDisp = false;
}
//------------------------------------------------------------------------------------------------------------------------------------------

GPS firmware

Arduino
/****************************************************************************************/
/*																						*/
/*									GPS  | �Kantooya									*/
/*																						*/
/*		Author:		Sam Farah															*/
/*		Email:		sam.farah1986@gmail.com												*/
/*		Website:	http://samfarah.net													*/
/*		Version:	1.0																	*/
/*		Description:																	*/
/*				The firmware for the ATMega microcontroller on board, it controls		*/
/*				the circuit, and handles reading inputs and sends commands to FSX.		*/
/*																						*/
/****************************************************************************************/

//-------------------------- Headers ----------------------------
#include <Quadrature.h> 
#include <AS1115.h>
#include <WSWire.h>
//---------------------------------------------------------------

//---------------------------- Pins -----------------------------
//			Name			Arduino Pin				Pin on ATMega
//		-----------			-----------				-------------
#define IRQ1					A0			//			 
#define Encoder1A				A3			//	
#define Encoder1B				A2			//	
#define Encoder2A				8			//	
#define Encoder2B				9			//	
//---------------------------------------------------------------

//------------------------ SETTINGS -----------------------------
#define HoldDelay				700
//---------------------------------------------------------------

//----------------------- Prototypes ----------------------------
class TSwitchCtrl;
uint8_t I2C_ClearBus();
//---------------------------------------------------------------

//------------------------- Objects -----------------------------
Quadrature Encoder1(Encoder1A, Encoder1B);
Quadrature Encoder2(Encoder2A, Encoder2B);
TSwitchCtrl *SwitchCtr;
AS1115 *SwitchCtr1;
//---------------------------------------------------------------

//---------------------- Public Variables -----------------------
int R1old;
int R2old;
int R1dif;
int R2dif;
unsigned long int HoldTime = 0;
bool ClearPressed = false;
//---------------------------------------------------------------

//-------------------------- Classes ----------------------------
class TSwitchCtrl
{
private:
	AS1115 * Chip;
	String Data;
	String OldData;
public:
	byte IRQ;
	byte LSB, MSB;
	TSwitchCtrl(uint8_t _IRQ, AS1115 *_Chip, uint8_t _LSB, uint8_t _MSB)
	{
		IRQ = _IRQ;
		Chip = _Chip;
		LSB = _LSB;
		MSB = _MSB;
		Data = "";
		Chip->begin(0x03,0x00);
	}
	void ReadSwitchs()
	{
		uint8_t ErrorCode;
		OldData = Data;
		Data = Chip->ReadKeysMul(&ErrorCode);// GetLeadingZeros(, 16);//potentially not needed (the Leading Zeros, already handled from library)
		if (ErrorCode >= 4)
		{
			I2C_ClearBus();
			Wire.begin();
		}
	}
	String GetData() { return Data; }
	String GetOldData() { return OldData; }
};
//---------------------------------------------------------------

//------------------------ Functions -----------------------------
String GetLeadingZeros(String Bin, short digits)
{
	String Padding = "";
	for (int i = 0; i < digits - Bin.length(); i++)Padding += "0";
	return Padding + Bin;
}
//---------------------------------------------------------------

//----------------------- Interrupt ----------------------------
ISR(PCINT1_vect) // handle pin change interrupt for D8 to D13 here
{
	interrupts();
	if (digitalRead(SwitchCtr->IRQ) == LOW)
	{

		SwitchCtr->ReadSwitchs();
		byte ChangedIndex = 0;
		char NewValue, InverseValue;
		for (byte i = 0; i < 16; i++)
			if (SwitchCtr->GetData()[i] != SwitchCtr->GetOldData()[i])
			{
				NewValue = SwitchCtr->GetData()[i];
				InverseValue = NewValue == '0' ? '1' : '0';
				ChangedIndex = i;
			}
		//Serial.println(ChangedIndex);
		//if (!MasterPower && ChangedIndex != 20) return;//if powered off, only listen to power swtich.
		if (NewValue == '1') //push buttons
		{
			switch (ChangedIndex)
			{
			case 1:  Serial.println("G11"); break;
			case 2:  Serial.println("G10"); break;
			case 3:  Serial.println("G12"); break;
			case 4:  Serial.println("G13"); break;
			case 5:	 HoldTime = millis(); ClearPressed = true; break;
			case 6:  Serial.println("G18"); break;
			case 7:  Serial.println("G19"); break;
			case 9:  Serial.println("G02"); break;
			case 10: Serial.println("G03"); break;
			case 11: Serial.println("G04"); break;
			case 12: Serial.println("G07"); break;
			case 13: Serial.println("G08"); break;
			case 14: Serial.println("G09"); break;
			}
		}
		else
		{
			if (ChangedIndex == 5)
			{
				ClearPressed = false;
				if (millis() - HoldTime < HoldDelay)Serial.println("G14");
			}
		}
	}
}

//******************************************************************************************************************************************
void setup() {
	pinMode(IRQ1, INPUT_PULLUP);
	Serial.begin(115200);
	SwitchCtr1 = new AS1115(0x00);
	SwitchCtr = new TSwitchCtrl(IRQ1, SwitchCtr1, 0, 15);
	delay(50);
	pciSetup(IRQ1);
	delay(50);
	SwitchCtr->ReadSwitchs();
	SwitchCtr->GetData();
}

void loop() {
	ENCODER();
	if (ClearPressed == true && millis() - HoldTime >= HoldDelay)
	{
		Serial.println("G15");
		ClearPressed = false;
	}
}
//******************************************************************************************************************************************

//------------------------------------------------------------------------------------------------------------------------------------------
void pciSetup(byte pin)
{
	*digitalPinToPCMSK(pin) |= bit(digitalPinToPCMSKbit(pin));  // enable pin
	PCIFR |= bit(digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
	PCICR |= bit(digitalPinToPCICRbit(pin)); // enable interrupt for the group
}
//------------------------------------------------------------------------------------------------------------------------------------------
void ENCODER() {
	int R1 = (Encoder1.position()); //The /2 is to suit the encoder
	if (R1 != R1old) { // checks to see if it different
		(R1dif = (R1 - R1old));// finds out the difference
							   //Serial.println(Encoder1.position());
		if (R1dif == 1) Serial.println("G22");
		if (R1dif == -1)Serial.println("G23");
		R1old = R1; // overwrites the old reading with the new one.
					//Disp->SetTempValChange(true);
	}
	int R2 = (Encoder2.position()); //The /2 is to suit the encoder
	if (R2 != R2old) { // checks to see if it different
		(R2dif = (R2 - R2old));// finds out the difference
		if (R2dif == 1) Serial.println("G20");
		if (R2dif == -1)Serial.println("G21");
		R2old = R2; // overwrites the old reading with the new one.
					//Disp->SetTempValChange(true);
	}

}
//------------------------------------------------------------------------------------------------------------------------------------------
uint8_t I2C_ClearBus() {
#if defined(TWCR) && defined(TWEN)
	TWCR &= ~(_BV(TWEN)); //Disable the Atmel 2-Wire interface so we can control the SDA and SCL pins directly
#endif

	pinMode(SDA, INPUT_PULLUP); // Make SDA (data) and SCL (clock) pins Inputs with pullup.
	pinMode(SCL, INPUT_PULLUP);
	delay(20);

	boolean SCL_LOW = (digitalRead(SCL) == LOW); // Check is SCL is Low.
	if (SCL_LOW) { //If it is held low Arduno cannot become the I2C master.
		return 1; //I2C bus error. Could not clear SCL clock line held low
	}

	boolean SDA_LOW = (digitalRead(SDA) == LOW);  // vi. Check SDA input.
	int clockCount = 20; // > 2x9 clock

	while (SDA_LOW && (clockCount > 0)) { //  vii. If SDA is Low,
		clockCount--;
		// Note: I2C bus is open collector so do NOT drive SCL or SDA high.
		pinMode(SCL, INPUT); // release SCL pullup so that when made output it will be LOW
		pinMode(SCL, OUTPUT); // then clock SCL Low
		delayMicroseconds(10); //  for >5uS
		pinMode(SCL, INPUT); // release SCL LOW
		pinMode(SCL, INPUT_PULLUP); // turn on pullup resistors again
									// do not force high as slave may be holding it low for clock stretching.
		delayMicroseconds(10); //  for >5uS
							   // The >5uS is so that even the slowest I2C devices are handled.
		SCL_LOW = (digitalRead(SCL) == LOW); // Check if SCL is Low.
		int counter = 20;
		while (SCL_LOW && (counter > 0)) {  //  loop waiting for SCL to become High only wait 2sec.
			counter--;
			delay(50);
			SCL_LOW = (digitalRead(SCL) == LOW);
		}
		if (SCL_LOW) { // still low after 2 sec error
			return 2; // I2C bus error. Could not clear. SCL clock line held low by slave clock stretch for >2sec
		}
		SDA_LOW = (digitalRead(SDA) == LOW); //   and check SDA input again and loop
	}
	if (SDA_LOW) { // still low
		return 3; // I2C bus error. Could not clear. SDA data line held low
	}

	// else pull SDA line low for Start or Repeated Start
	pinMode(SDA, INPUT); // remove pullup.
	pinMode(SDA, OUTPUT);  // and then make it LOW i.e. send an I2C Start or Repeated start control.
						   // When there is only one I2C master a Start or Repeat Start has the same function as a Stop and clears the bus.
						   // A Repeat Start is a Start occurring after a Start with no intervening Stop.
	delayMicroseconds(10); // wait >5uS
	pinMode(SDA, INPUT); // remove output low
	pinMode(SDA, INPUT_PULLUP); // and make SDA high i.e. send I2C STOP control.
	delayMicroseconds(10); // x. wait >5uS
	pinMode(SDA, INPUT); // and reset pins as tri-state inputs which is the default state on reset
	pinMode(SCL, INPUT);
	//Serial.println("I2C Recovered");
	return 0; // all ok
}
//------------------------------------------------------------------------------------------------------------------------------------------

Credits

Sam Farah

Sam Farah

0 projects • 8 followers
Software Developer, Electronics Engineer Technology, Computer Science

Comments