Hackster is hosting Impact Spotlights: Smart Home. Watch the stream live on Thursday!Hackster is hosting Impact Spotlights: Smart Home. Stream on Thursday!
dbeacroft
Published © MPL-2.0

AI-9 Gas Turbine ECU Control System (Jet Engine)

Start a Helicopter Gas Turbine APU engine using an Arduino Uno, This is what they were made for !

AdvancedFull instructions providedOver 6 days2,194
AI-9 Gas Turbine ECU Control System (Jet Engine)

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
This will be the core processor to manage the relays and monitor sensors.
×1
Arduino Nano Every
Arduino Nano Every
Due to the TFT library being so big, i needed extra ram and something to off load the graphics draw routines.
×1
Prototyping Prototype Shield Mini Breadboard 5V 1A
This helps keep all the wiring neat and if i needed replacing the Arduino is easy
×1
3.5" inch Touch Screen TFT LCD 480x320 ILI9488 SPI Display 3.3V 5V
Graphics display to output diagnostics, time stamp and sensor information
×1
XL4015 5A DC-DC Step Down Adjustable Power Supply Module Buck Converter
×1
Geekcreit® 5V 4 Channel Relay Module For PIC ARM DSP AVR MSP430
×1
Geekcreit 2 Channel Relay Module With Optocoupler For PIC AVR DSP ARM
×1
Optocoupler DIP-4 PC817C
Keep the 24v away from the electronics
×4
Grove - 1-Wire Thermocouple Amplifier (MAX31850K)
Seeed Studio Grove - 1-Wire Thermocouple Amplifier (MAX31850K)
Required to read gas temperature from the thermocouples
×1

Hand tools and fabrication machines

Workshop, Milling Mc, Router and general tooling

Story

Read more

Custom parts and enclosures

AI-9 Russian Gas Turbine (APU)

Ok this is not off the shelf (certainly not any more) but necessary for the project

Finger Plate

Cad layout of the Finger plate for routing

Finger Plate

Make things look cool, I used a small CNC router to produce the finger plate with decals and cut outs.

Enclosure

I wanted a military look, in my head it looked bigger, on assembly i realy struggled to get it all in

Mounting Board

Nothing special, 2mm galvanized plate, but it became a jigsaw to work out where it all goes

TFT Display

Using the ILI9488.h library in an Arduino pretty much used up all the ram, so i used a nano every (bit more ram available) and connected it vai SPI, this gave me a graphics controller with its own GUI . you have to build the GUI but it was reasonably strait forward.

Schematics

Nano to TFT

Direct connection of the Nano Every for the TFT, This uses an SPI connection to the TFT

Original Control System

This provides the timing and In/Out ports required

New Control system

Arduino control lines and wiring centre

Code

Main Control System

Arduino
I have never done this before and if i were to have another go, im sure i would do better. That said it does work and to test it i built another diagnostics box that responds the same way the engine would (Stops the missus shouting at me making all that noise)

Its basically a huge time dependant loop
I have used direct hardware ports to trigger the relay as talking to the SPI bus takes time and the Thermocouple read is slow (i may have to re look at that)
/*
  AI-9 APU Gas Turbine Launch (Main Control Panel)
=====================================================================
  
  Created 22/08/2021
  by Derek Beacroft UK

// Run          fuel & Ignition
// False Start  Fuel No Ignition
// Cold Scroll  No Fuel No Ignition

Display on I2C Line
  CommandNo,"buff",Val
  1,"",0        // Start
  2,"",0        // Stop
  3,"",0        // Reset Display
  4,"message",0 // Display new message 
  5, 
  6,xxx         // Temperature
  7,xxx         // Speed
  8,
  9,
  10,"",01     // Over Speed
  11,"",01     // Normal Speed
  12,"",01     // Oil Pressure
  13,"",01     // Centrifuge
  14,"",01     // Boost Pump
   
*/
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>


// Data wire is plugged into port 3 on Arduino
#define ONE_WIRE_BUS 15  // 15=A1
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);


#define Debug (bool) false      //
int l_time = millis()/1000;

bool start = false;
bool R1_eMotor = false;
bool R2_eMotor = false;
bool R3_Ignition = false;
bool R4_FuelStart = false;
bool R5_FuelRun= false;
bool R6_Boost = false;

bool Sence_OpenCentrifuge =false;  // eMotor Switch Nc (default open until connected to engine)
bool Sence_SpeedOk = false;         // Fuel Pressure ok
bool Sence_OilOk = false;           // Oil Pressure ok
bool Sence_OverSpeed = false;       // Fuel Pressure over

bool ColdScroll = false;
bool FalseStart = false;
bool BtnHeld = false;


int val =0;
int Value =0;
int Sence_Temp=240;
int Btn =0;
unsigned int ReadBtn;

unsigned long c = 0;
unsigned long Temperature = 25;

long StartupDebounceTime= 0;        // the last time the output pin was toggled
long lastDebounceTime = 0;          // the last time the output pin was toggled
#define debounceDelay (uint8_t) 400 // the debounce time; increase if the output flickers


// constants won't change
const int PIN02_R1_eMotor1 = 2;     // Start 1
const int PIN03_R2_eMotor2 = 3;     // Start 2
const int PIN04_R3_Ignition = 4;    // Ignition
const int PIN05_R4_StartFuel = 5;   // Fuel Start
const int PIN10_R5_RunFuel = 10;    // Fuel Run
const int PIN11_R6_Boost = 11;      // Boost Pump

const int PIN06_Overspeed = 6;      // Over Speed
const int PIN07_NormalRPM = 7;      // Normal RPM
const int PIN08_NormalOil = 8;      // Oil Pressure OK
const int PIN09_Centrifuge = 9;     // Starter CuttOff
const int PIN12_StartBtn = 12;      // START

const int PINA0_RPM     = A0;       // RPM
const int PINA1_Temp    = A1;       // Exhaust Temperature
const int PINA2_Buttons = A2;       // Buttons

char newtext[]="123";


void directWrite(uint8_t pin, uint8_t value);

struct PinInfo {
  volatile uint8_t *reg;
  uint8_t mask;
};
const PinInfo unoPins[] = {
  {&PORTD, (uint8_t) 0b11111110},
  {&PORTD, (uint8_t) 0b11111101},
  {&PORTD, (uint8_t) 0b11111011},
  {&PORTD, (uint8_t) 0b11110111},
  {&PORTD, (uint8_t) 0b11101111},
  {&PORTD, (uint8_t) 0b11011111},
  {&PORTD, (uint8_t) 0b10111111},
  {&PORTD, (uint8_t) 0b01111111},
  {&PORTB, (uint8_t) 0b11111110},
  {&PORTB, (uint8_t) 0b11111101},
  {&PORTB, (uint8_t) 0b11111011},
  {&PORTB, (uint8_t) 0b11110111},
  {&PORTB, (uint8_t) 0b11101111},
  {&PORTB, (uint8_t) 0b11011111},
  {&PORTC, (uint8_t) 0b11111110},
  {&PORTC, (uint8_t) 0b11111101},
  {&PORTC, (uint8_t) 0b11111011},
  {&PORTC, (uint8_t) 0b11110111},
  {&PORTC, (uint8_t) 0b11101111},
  {&PORTC, (uint8_t) 0b11011111},
};


 
// the setup function runs once when you press reset or power the board
void setup() {
  Serial.begin(115200);
  //
  Wire.begin();        // join i2c bus (address optional for master)
  //Wire.delay(200);
  sensors.begin();  // Thermocouple    

  pinMode(PIN02_R1_eMotor1, OUTPUT);
  pinMode(PIN03_R2_eMotor2, OUTPUT);  
  pinMode(PIN04_R3_Ignition, OUTPUT);  
  pinMode(PIN05_R4_StartFuel, OUTPUT);  
  pinMode(PIN10_R5_RunFuel, OUTPUT);  
  pinMode(PIN11_R6_Boost, OUTPUT); 

  pinMode(PIN06_Overspeed, INPUT_PULLUP);
  pinMode(PIN07_NormalRPM, INPUT_PULLUP);
  pinMode(PIN08_NormalOil, INPUT_PULLUP);
  pinMode(PIN09_Centrifuge, INPUT_PULLUP);
  pinMode(PIN12_StartBtn, INPUT_PULLUP);

  pinMode(PINA0_RPM, INPUT);     // RPM
  pinMode(PINA1_Temp, INPUT);    // Temperature
  pinMode(PINA2_Buttons, INPUT); // buttons
  
  // initialize digital pin as an output. (Default to High will disable relay)
  //digitalWrite(PIN02_R1_eMotor1, HIGH); 
  //digitalWrite(PIN03_R2_eMotor2, HIGH); 
  //digitalWrite(PIN04_R3_Ignition, HIGH); 
  //digitalWrite(PIN05_R4_StartFuel, HIGH); 
  //digitalWrite(PIN10_R5_RunFuel, HIGH); 
  //digitalWrite(PIN11_R6_Boost, HIGH); 

  directWrite(PIN02_R1_eMotor1, HIGH); 
  directWrite(PIN03_R2_eMotor2, HIGH); 
  directWrite(PIN04_R3_Ignition, HIGH); 
  directWrite(PIN05_R4_StartFuel, HIGH); 
  directWrite(PIN10_R5_RunFuel, HIGH); 
  directWrite(PIN11_R6_Boost, HIGH); 

  //const unsigned short int SW = 4;
  // may only work on pin 2/3
  //attachInterrupt(digitalPinToInterrupt(SW_PIN13_1), changeLock, FALLING);
  
  //delay(1000);
  //Serial.println(digitalRead(PIN06_Overspeed));
  //Serial.println(digitalRead(PIN08_NormalOil));
  InfoMessage("Controller Startup",true);
  // default centrifuge to Nc
  DisplayUpdate(13,"",0);
  //
  // wait for Display to Initialise
  delay(5000);      
  //  
  //// 
  //// Get any Slave data eg Version info or ready !
  //Wire.requestFrom(8, 6);    // request 6 bytes from slave device #8
  //while (Wire.available()) { // slave may send less than requested
  //    char c = Wire.read(); // receive a byte as character
  //    Serial.print(c);         // print the character
  //  }  
}

//
// the loop function runs over and over again forever
void loop() {
//
// 1 second interval Timer
  if ((((millis()-c)/1000) > l_time) && (start==false))
   {  
     l_time=((millis()-c)/1000);
     GetTemperature();
   }
    //
    //  Read Engine Sensors
    //  =========================================================
    if (Debug==false)
      {          
        //Sence_Temp=analogRead(PINA1_Temp);  
        bool BitCheck;        
        
        // Over Speed (No false)
        //if (digitalRead(PIN06_Overspeed)==LOW){Sence_OverSpeed=true;} else {Sence_OverSpeed=false;}
        BitCheck=digitalRead(PIN06_Overspeed);
        if (BitCheck==LOW && Sence_OverSpeed==false)
          {Sence_OverSpeed=true;DisplayUpdate(12,"",0);} 
        else if (BitCheck==HIGH && Sence_OverSpeed==true)
          {Sence_OverSpeed=false;DisplayUpdate(12,"",1);}
        
        // Normal RPM (No false)
        BitCheck=digitalRead(PIN07_NormalRPM);
        if (BitCheck==LOW && Sence_SpeedOk==false) 
          {Sence_SpeedOk=true;DisplayUpdate(11,"",0);}
        else if (BitCheck==HIGH && Sence_SpeedOk==true)
          {Sence_SpeedOk=false;DisplayUpdate(11,"",1);}
        
        // Oil Pressure (No false)
        BitCheck=digitalRead(PIN08_NormalOil);
        if (BitCheck==LOW && Sence_OilOk==false)
          {Sence_OilOk=true;DisplayUpdate(10,"",0);} 
        else if (BitCheck==HIGH && Sence_OilOk==true)
          {Sence_OilOk=false;DisplayUpdate(10,"",1);} 

        // Note Cetrifuge is Fliped (Nc false)
        BitCheck=digitalRead(PIN09_Centrifuge);
        if (BitCheck==LOW && Sence_OpenCentrifuge==true)
          {Sence_OpenCentrifuge=false;DisplayUpdate(13,"",1);} 
        else if (BitCheck==HIGH && Sence_OpenCentrifuge==false)
          {Sence_OpenCentrifuge=true;DisplayUpdate(13,"",0);}
        //
        //if (Sence_SpeedOk==true) {InfoMessage("speed ok");}
        //DisplayUpdate(10,"",0);
               
      }
    //
    // Read Analogue Buttons 
    ReadBtn=analogRead(PINA2_Buttons);  
    //filter out any noise by setting a time buffer
    //Serial.println(ReadBtn);
    if ((millis() - lastDebounceTime) > debounceDelay )    
      {    
        Btn=ReadBtn;    
        //InfoMessage("Press " +(String)ReadBtn);                       
        //        
        if (start==false)
          {
            //if (Btn <100) {FalseStart=false;ColdScroll=false;}
            if (Btn <100 && (FalseStart==true || ColdScroll==true)){InfoMessage("Normal Start",true);FalseStart=false;ColdScroll=false;}        
            
            if (Btn >100 && Btn <300 && FalseStart==false){InfoMessage("False Start",true);FalseStart=true;ColdScroll=false;}        
            if (Btn >300 && Btn <500 && ColdScroll==false){InfoMessage("ColdScroll",true);FalseStart=false;ColdScroll=true;}    
            if (Btn >500 && Btn <750){InfoMessage("Test Spare"+(String)ReadBtn,true);}                                   
            if (Btn >750 && Btn <950)
              {
                //
                DisplayUpdate(3,"",0) ; // Reset Display
                //
                Reset_Engine();   
                //InfoMessage("Ready",true);               
                start=false;                
              }    
          }
        else
          {           
            // Stop button press while starting/running         
            if (Btn >950)
            {
              c = 0;    
              start=false;
              Shutdown();   
              //newtext = "ab";
//https://forum.arduino.cc/t/tricks-to-reduce-compiled-size/43560/40

              InfoMessage("Stop "+(String)ReadBtn,true);                     
            }
            //Serial.println(Btn);          
          }
        //ReadBtn=0;
        lastDebounceTime = millis();
      }

  //
  // Start Button Press for 3 sec
  // ======================================================================
  if (digitalRead(PIN12_StartBtn)==LOW && start == false && Sence_OverSpeed==false) 
    {   
      // add delay to allow any Analog reads to stabalise
      delay(50);      
      //    
      if (BtnHeld==false){BtnHeld=true;StartupDebounceTime = millis();} 
      //
      if ((millis() - StartupDebounceTime) > 2000 and BtnHeld==true)    
        {
          BtnHeld=false;          
          StartupDebounceTime = millis();
          //          
          c = millis();  // call before EngineReset
          Reset_Engine();
          //DisplayUpdate(3," ",0);     // Reset Display             
          //
          
          if (FalseStart==false && ColdScroll==false) {InfoMessage("Launch Start",true);}    
          else if (FalseStart==true) { InfoMessage("False Start",true); }          
          else if (ColdScroll==true) { InfoMessage("Cold Scroll",true); }          
          //
          delay(50);      
          start = true;          
          DisplayUpdate(1,"",0);
          c = millis();  
          //
          // Boost pump is allways enabled, (notifies the diags we are running)
          R6_Boost= true;          
          //digitalWrite(PIN11_R6_Boost, LOW);  
          directWrite(PIN11_R6_Boost, LOW);  
          DisplayUpdate(14,"",0);

          InfoMessage("R6, Boost Pump On",true);                       
          DisplayUpdate(1,"",0);
        }
    }
  else
    {
      // No Press
      BtnHeld=false;   
      //startupDebounceTime = millis();
    }

  // 
  // Engine Launch
  if (start==true) 
    // Read Engine Sensors and Status
    // =====================================================
    {   // Must allways monitor for STOP press
      if (analogRead(PINA2_Buttons) >950)
        {
          c = 0;
          start=false;
          Shutdown();   
          InfoMessage("Stop Button",true); 
        }
      //  
      // Get Temperature 
      if ((millis() - c) == 8000){GetTemperature();}
      if ((millis() - c) == 11000){GetTemperature();}
      if ((((millis()-c)/1000) > l_time) && ((millis() - c) >= 15000) )
        {
          l_time=((millis()-c)/1000);
          GetTemperature();      
        }
      //
      // Early initialisation tests
      // Boost pump to pressure (5 Seconds)
      if ((millis()-c) > 0 && (millis() - c) < 5000)
        {
          if (R6_Boost==false)
            {
              Reset_Engine();
              InfoMessage("BoostPump Disabled",true);
            }

          //if (Sence_OpenCentrifuge == true)
          if (Sence_OpenCentrifuge == true)
            {
              Reset_Engine();
              InfoMessage("eMotor Centrifuge Fault",true);
            }
          if (Sence_OilOk == true)
            {
              Reset_Engine();
              InfoMessage("Oil Pressure Sensor Fault",true);
            }
          if (Sence_SpeedOk == true)
            { 
              Reset_Engine();
              InfoMessage("Speed Sensor Fault",true);
            }
          if (Sence_OverSpeed == true)
            {
              Reset_Engine();
              InfoMessage("OverSpeed Sensor Fault",true);
            }
           
        }
      // Relay R1, eStart #1
      if ((millis() - c) >= 5000 && (millis() - c) <= 5020)
        {    
          // eMotor Start #1 @5 sec
          if (R1_eMotor == false)
            {
              R1_eMotor = true;                
              //digitalWrite(PIN02_R1_eMotor1, LOW);  
              directWrite(PIN02_R1_eMotor1, LOW);  
              InfoMessage("R1, eMotor1 Start",false);
            }          
        // Relay R3, Ignition 
        if (R3_Ignition == false && ColdScroll==false && FalseStart==false)
          {       
            R3_Ignition = true;          
            //digitalWrite(PIN04_R3_Ignition, LOW);  
            directWrite(PIN04_R3_Ignition, LOW);  
            InfoMessage("R3, Ignition On",false);
          }
        // Relay R4 Fuel Start 
        if (R4_FuelStart== false && ColdScroll==false )//&& FalseStart==false
          {
            R4_FuelStart= true;
            //digitalWrite(PIN05_R4_StartFuel, LOW);    
            directWrite(PIN05_R4_StartFuel, LOW);    
            InfoMessage("R4, Fuel Start On",false);
          }          
        }       
      // Serial.println("..");
      // Relay R5,Fuel Run 
      if ((millis() - c) >= 6000 && (millis() - c) <= 6020)
        {
          //Serial.println("-----------------------------------");
          // Fuel run @6 sec
        if (R5_FuelRun == false && ColdScroll==false)//and FalseStart==false
          {       
            R5_FuelRun = true;          
            //digitalWrite(PIN10_R5_RunFuel, LOW);  
            directWrite(PIN10_R5_RunFuel, LOW);  
            InfoMessage("R5, Fuel Run On",false);         
          }
        }

      // Relay R2, Starter2
      if ((millis() - c) >= 6500 && (millis() - c) <= 6520 && R2_eMotor == false)
        {  // Full Amps to motor 
          //if (R2_eMotor == false)
          //  {       
          R2_eMotor = true;          
          //digitalWrite(PIN03_R2_eMotor2, LOW);  
          directWrite(PIN03_R2_eMotor2, LOW);  
          InfoMessage("R2, eMotor2 Start",false);         
          //  }
        }
      // Relay R1 Starter1
      if ((millis() - c) >= 6800 && R1_eMotor == true)   
        {
          // Half motor Power Disable (Disabled to stop burnout if R2 Fails)
          //if (R1_eMotor == true)        
          //{
          R1_eMotor = false;          
          //digitalWrite(PIN02_R1_eMotor1, HIGH);  
          directWrite(PIN02_R1_eMotor1, HIGH);  
          InfoMessage("R1, eMotor1  Disabled",false); 
          //}
        }        
                    
      // Realy R4,Starting Fuel &  Relay R3, Ignition Coil
      if ((millis() - c) >= 12000)
        {
          if (R4_FuelStart== true)
            {          
              // Check tart Fuel has stoped @12 sec
              R4_FuelStart= false;          
              //digitalWrite(PIN05_R4_StartFuel, HIGH);           
              directWrite(PIN05_R4_StartFuel, HIGH);           
              InfoMessage("R4, Fuel Start Off",false);
            }          
          if (R3_Ignition==true)
            {
              // Check Ignition Off @12 Sec
              R3_Ignition = false;        
              //digitalWrite(PIN04_R3_Ignition, HIGH);
              directWrite(PIN04_R3_Ignition, HIGH);
              InfoMessage("R3, Ignition Shutoff",false);
            } 
        }                

      //
      // Constant Monitoring From Here
      // ========================================================================
      // Over Temperature (Any Time)
      if (Sence_Temp >800 && start==true)
        {          
          InfoMessage("Over Temperature",true);
          Shutdown();
        } 
      
      //
      // RPM Over Speed  (Any Time after 20 sec)
      if ((millis() - c) >= 20000 && start==true && Sence_OverSpeed==true)      
        {
          InfoMessage("Over Speed",true);     
          Shutdown();
        }
      
      // RPM not to speed  after 20s   
      if ((millis() - c) >= 20000 && start==true && Sence_SpeedOk==false)      
        {    
//if (digitalRead(PIN_D8_NormalOil)==LOW){InfoMessage("Oil Signal ok -------------------");}      
//if (digitalRead(PIN_D7_NormalRPM)==LOW){InfoMessage("RPM Signal ok -------------------");}           
          InfoMessage("Low RPM Detection",true);
          Shutdown();
        }
      // Oil Pressure Low after 24s   
      if ((millis() - c) >= 24000 && start==true && Sence_OilOk==false && Sence_SpeedOk==true)      
        {    
//if (digitalRead(PIN_D8_NormalOil)==LOW){InfoMessage("Oil Signal ok -------------------");}      
//if (digitalRead(PIN_D7_NormalRPM)==LOW){InfoMessage("RPM Signal ok -------------------");}           
          InfoMessage("Oil Pressure Low",true);
          Shutdown();
        }    

      // Sence_OpenCentrifuge after 20s   
      if ((millis() - c) >= 20000 && start==true && Sence_OpenCentrifuge==false)      
        {          
          InfoMessage("eMotor Centrifuge Error",true);
          Shutdown();
        }  


      // Stop motor if centrifuge after 12s or max >20s
      if ((millis() - c) >= 12000 && Sence_OpenCentrifuge==true && R2_eMotor==true && start==true)        
        {          
          R2_eMotor =false;
          //digitalWrite(PIN03_R2_eMotor2, HIGH);
          directWrite(PIN03_R2_eMotor2, HIGH);
          InfoMessage("eMotor2 disconnect",true);
        }    
      if ((millis() - c) >= 20000 && start==true && R2_eMotor ==true)
        {          
          R2_eMotor =false;
          //digitalWrite(PIN03_R2_eMotor2, HIGH);
          directWrite(PIN03_R2_eMotor2, HIGH);
          InfoMessage("eMotor2 disconnect",true);
        }            
    }

// End Of Loop
}


bool GetTemperature()
{
   sensors.requestTemperatures(); // Send the command to get temperatures     
   Sence_Temp=sensors.getTempCByIndex(0);
   DisplayUpdate(6,"",Sence_Temp);  
  }

bool Shutdown()
  {
  c = 0;    
  Reset_Engine();       
  DisplayUpdate(2," ",0);     // Stop Timer
  }

bool Reset_Engine()
  {
  // Shutdown Engine
  start = false;
  //c = 0; 
  
  //Sence_Temp=0;
  Sence_OverSpeed = false;
  Sence_SpeedOk = false;
  Sence_OilOk = false;
  Sence_OpenCentrifuge =false;
  
  directWrite(PIN02_R1_eMotor1, HIGH);   R1_eMotor = false;
  directWrite(PIN03_R2_eMotor2, HIGH);   R2_eMotor = false;
  directWrite(PIN04_R3_Ignition, HIGH);  R3_Ignition = false;  
  directWrite(PIN05_R4_StartFuel, HIGH); R4_FuelStart = false;
  directWrite(PIN10_R5_RunFuel, HIGH);   R5_FuelRun = false;
  directWrite(PIN11_R6_Boost, HIGH);     R6_Boost =false;



  
  
  // reset of display will clear all sensors
//DisplayUpdate(3,"",0) ;
  
  delay(200);  
  InfoMessage("Engine Reset",false);      
  return true;
  }
//
//
void InfoMessage(String NewText,boolean Display)
  {
  //Serial.println( (String) (millis() - c) +":" +NewText);
  //  
  //DisplayUpdate(4,NewText,0) ; // 4 Message out to Display
  
  if (Display==true)
    {
      // TFT output
      DisplayUpdate(4,NewText,0) ; // 4 Message out to Display
    }
    else
    {
      // Debug only
      Serial.println(NewText);
    }   
  }  
//
//https://docs.arduino.cc/library-examples/wire-library/ControllerWriter
void DisplayUpdate(int CommandNo,String Buffer,int Val)
{
  int mask = 0xFF;
  Serial.println("Command:" +(String)CommandNo+ " "+Buffer +" "+(String)Val);
  //return;  
  Wire.beginTransmission(8);  // transmit to device #8
  Wire.write(CommandNo);      // sends one byte Command 1-255  
  Wire.write(Buffer.c_str()); // Send Buffer
  //Wire.write("");            // Terminate Buffer with ""
  Wire.write(Val & mask);     // sends one byte Val
  Wire.write(Val >>8);        // sends one byte Val
  Wire.endTransmission();     // stop transmitting
  //Serial.println("Done");
  }

  
void setpin(int Pin)
  {
    //https://arduino.stackexchange.com/questions/44531/arduino-esp8266-direct-fast-control-of-the-digital-pins
    bitSet(PORTD,Pin);
    bitSet(PORTD,Pin);
    bitSet(PORTD,Pin);
    bitSet(PORTD,Pin);
    
  }


void directWrite(uint8_t pin, uint8_t value) {
  if (value == 0) {
    *(unoPins[pin].reg) &= unoPins[pin].mask;
  } else {
    *(unoPins[pin].reg) |= ~unoPins[pin].mask;
  }
}
  

Display Controler

Arduino
This created the main display UI, has a small update window to show messages, Run timer and Relay status
And before im flooded with "why not do it this way" etc, its because i did and i do it for pleasure.
/*
  AI-9 APU Gas Turbine Display
  =====================================================================
  
  Created 22/08/2021
  by Derek Beacroft

Display on I2C Line
  CommandNo,"buff",Val
  1,"",0        // Start
  2,"",0        // Stop
  3,"",0        // Reset Display
  4,"message",0 // Display new message 
  5, 
  6,xxx         // Temperature
  7,xxx         // Speed
  8,
  9,
  10,"",01     // Over Speed
  11,"",01     // Normal Speed
  12,"",01     // Oil Pressure
  13,"",01     // Centrifuge
  14,"",01     // Boost Pump
  
    free 399 
   
*/
#include <Wire.h>
//
//
#include <LCDWIKI_GUI.h> //Core graphics library
#include <LCDWIKI_SPI.h> //Hardware-specific library
//paramters define
#define MODEL ILI9488_18
//
#include <arduino-timer.h>
//auto timer = timer_create_default(); // create a timer with default settings


//https://www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html




//#define CS   A2    
//#define CD   A3
//#define RST  A1
//#define LED  A0   //if you don't need to control the LED pin,you should set it to -1 and set it to 3.3V

#define CS   A2    
#define CD   A0
#define RST  A1
#define LED  A3   //if you don't need to control the LED pin,you should set it to -1 and set it to 3.3V

#define Rad (float) 0.01745329

unsigned long RunTime = 0;
bool SystemRun = false;
bool Dev = false;

//the definiens of hardware spi mode as follow:
//if the IC model is known or the modules is unreadable,you can use this constructed function
LCDWIKI_SPI my_lcd(MODEL,CS,CD,RST,LED); //model,cs,dc,reset,led
 
//define some colour values
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

//#define Op (String) "Oil Pressure:"
//#define Space (String) "                    "

int Tm;
int Ts;
int TempPointer = 300;              // Initial point of Temp gauge
//bool Ready = false;                 // Display Initialised and Ready
bool DisplayReady = false;           // Display Initialised and Ready

String Line1="";
String Line2="";//"Initialising";
//String Line3="AI-9 Control v1.0";
String Line3="";
String Line4="";
//String Line5="";
//char abc(20);





void setup() {
  //
  //pinMode(LED, OUTPUT);     // TFT Backlite
  //analogWrite(LED, 250);    // Power level (Max 3v)
  Serial.begin(115200);           // start serial for output 
  
  //
  Wire.begin(8);                // join i2c bus with address #8
  //Wire.onRequest(requestEvent); // register outgoung event
  Wire.onReceive(receiveEvent); // register incommong event

  my_lcd.Init_LCD();
  my_lcd.Fill_Screen(0x0); 
  //Serial.println("Init");
  
my_lcd.Led_control(true);
  //
  // left,top,left+,right+
  //mylcd.Fill_Screen(BLACK);
  my_lcd.Set_Rotation(1);  
  my_lcd.Set_Text_Back_colour(BLACK);
  my_lcd.Set_Text_colour(0xE000);
    
  //my_lcd.Print_String("Initialising AI-9", 10, 10);
  my_lcd.Set_Text_Size(4);
  my_lcd.Print_String("RUN TIME", 10, 10);  
  my_lcd.Set_Text_Size(5);
  my_lcd.Print_String("00:00", 30, 55);
  
  my_lcd.Set_Text_Size(2);
  my_lcd.Draw_Rectangle( 0,  0,  477, 317);
  my_lcd.Draw_Rectangle( 1,  1,  476, 316);
  my_lcd.Draw_Rectangle( 2,  2,  475, 315);
  
  my_lcd.Draw_Rectangle( 30,  140,  450, 141);
  my_lcd.Print_String("Oil Pressure", 270, 15);  
  my_lcd.Draw_Rectangle( 430,  10, 460, 30);
  
  my_lcd.Print_String("Normal Speed", 270, 40);  
  my_lcd.Draw_Rectangle( 430,  35, 460, 55);
  
  my_lcd.Print_String("Over Speed", 290, 65);  
  my_lcd.Draw_Rectangle( 430,  60, 460, 80);
  
  my_lcd.Print_String("Centrifuge", 290, 90);    
  my_lcd.Draw_Rectangle( 430,  85, 460, 105);
    
  my_lcd.Print_String("Pump:", 280, 115);    
  my_lcd.Draw_Rectangle( 430,  110, 460, 130);

  my_lcd.Print_String("Telemetry", 10, 150);  
  my_lcd.Draw_Rectangle( 10,  170, 260, 300);  
  //
  // Add Startup Initalising text
  //DisplayMessage(""); 
  //
  // Temp 0 to 900 deg
  my_lcd.Draw_Circle(365,230,18);
  my_lcd.Draw_Circle(365,230,50);
  my_lcd.Draw_Circle(365,230,70);  
  //
  my_lcd.Set_Text_Size(1);
  my_lcd.Print_String("0", 280, 250);  
  my_lcd.Print_String("200", 370, 150);  
  my_lcd.Print_String("650", 440, 210);  
  my_lcd.Print_String("830", 420, 280);  
  my_lcd.Print_String("1000", 345, 305);  
  my_lcd.Draw_Rectangle( 10,  170, 260, 300);
     
  
  //my_lcd.Draw_Line(365,260,365,300);
 // (x,y) = (12*sin(115), 12*cos(115))
  //my_lcd.Draw_Line(30*sin(Deg)+365,30*cos(Deg)+230,70*sin(Deg)+365,70*cos(Deg)+230);

  my_lcd.Set_Draw_color(RED);
  
  for (float  T = 0; T<300; T+=1)
  {
    //my_lcd.Draw_Line((30*sin(T*Rad ))+365,(30*cos(T*Rad))+230,(70*sin(T*Rad))+365,(70*cos(T*Rad))+230);
    my_lcd.Draw_Line((50*sin(T*Rad ))+365,(50*cos(T*Rad))+230,(69*sin(T*Rad))+365,(69*cos(T*Rad))+230);    

if ( T ==50)my_lcd.Set_Draw_color(GREEN);
if ( T ==100)my_lcd.Set_Draw_color(BLUE);
//if ( T ==150)my_lcd.Set_Draw_color(RED);

    //Serial.println((String) (T) +" x:"+(String) ((30*sin(T*Rad))) + " y:"+ (String) ((30*cos(T*Rad))) + " :" + (String) (sin(T*Rad))  );
    //(30*sin(T))+365,(30*cos(T))+230,(70*sin(T))+365,(70*cos(T))+230)
  }

my_lcd.Set_Draw_color(RED);
my_lcd.Draw_Line((46*sin(50*Rad ))+365,(46*cos(50*Rad))+230,(49*sin(50*Rad))+365,(49*cos(50*Rad))+230);
my_lcd.Draw_Line((46*sin(100*Rad ))+365,(46*cos(100*Rad))+230,(49*sin(100*Rad))+365,(49*cos(100*Rad))+230);
my_lcd.Draw_Line((46*sin(150*Rad ))+365,(46*cos(150*Rad))+230,(49*sin(150*Rad))+365,(49*cos(150*Rad))+230);
my_lcd.Draw_Line((46*sin(200*Rad ))+365,(46*cos(200*Rad))+230,(49*sin(200*Rad))+365,(49*cos(200*Rad))+230);
my_lcd.Draw_Line((46*sin(250*Rad ))+365,(46*cos(250*Rad))+230,(49*sin(250*Rad))+365,(49*cos(250*Rad))+230);
my_lcd.Draw_Line((46*sin(300*Rad ))+365,(46*cos(300*Rad))+230,(49*sin(300*Rad))+365,(49*cos(300*Rad))+230);

//uint16_t x,y;
//x=10;
//y=100;
//  
// for (int16_t row = 0; row < 200; row++) 
//      {
//            my_lcd.Read_GRAM(x, y + row, buf,wid, 1);
//            my_lcd.Set_Addr_Window(x, y + row, x + wid - 1, y + row);
//            my_lcd.Push_Any_Color(buf + dx, wid - dx, 1,0);
//            my_lcd.Push_Any_Color(buf + 0, dx, 0,0);
//        }

//
//
 timer.every(1000, DisplayTime);
  //
  // Display layout Initalised, allow messages
  DisplayReady=true;

  //
  // Move Temperature Display 
  for (float  T = 1000; T>=0; T-=8)
    {DisplayTemperature(T);}
    //DisplayTemperature(0);

//    my_lcd.Set_Text_colour(WHITE);
//    TempPointer=0;
//    my_lcd.Set_Draw_color(WHITE);
//    my_lcd.Draw_Line((20*sin(300*Rad ))+365,(20*cos(300*Rad))+230,(45*sin(300*Rad))+365,(45*cos(300*Rad))+230);
//    my_lcd.Draw_Line((20*sin(299*Rad ))+365,(20*cos(299*Rad))+230,(45*sin(299*Rad))+365,(45*cos(299*Rad))+230);


      //my_lcd.Fill_Triangle(50,50,75,25,100,50);
      //my_lcd.Draw_Triangle
//      my_lcd.Fill_Triangle(my_lcd.Get_Display_Width()/2-1,my_lcd.Get_Display_Height()/2-1-i,
//                   my_lcd.Get_Display_Width()/2-1-i,my_lcd.Get_Display_Height()/2-1+i,
//                   my_lcd.Get_Display_Width()/2-1+i,my_lcd.Get_Display_Height()/2-1+i);  




  //my_lcd.Set_Text_colour(WHITE);  
  //
  //
  //RunTime = millis();  // call before Start


}


//
// the loop function runs over and over again forever
// ========================================================================
void loop() {

// abc='Oil Pressure';
// my_lcd.Print_String(abc, 270, 15);  

  //if (SystemRun == true)  { DisplayTime();}

  // if (((millis()-c)/1000) > l_time)
  // {}

timer.tick(); // tick the timer

//  if (millis()/1000 ==15) SetSensor(1,true);
//  if (millis()/1000 ==20) SetSensor(1,false);
//
//  if (millis()/1000 ==15) SetSensor(3,true);
//  if (millis()/1000 ==30) SetSensor(3,false);
//  
//  DisplayMessage((String) millis());

//  Serial.println((String) millis());

//uint16_t scrollbuf[my_lcd.Get_Display_Height()]; 
//
//  for (int16_t i = my_lcd.Get_Display_Width(), dx = 4, dy = 0; i > 0; i -= dx)
//        {
//             windowScroll(0, 216, my_lcd.Get_Display_Width(), 16, dx, dy, scrollbuf);
//        }


}
  

////
//// function that executes whenever data is requested by master
//// this function is registered as an event, see setup()
//void requestEvent() {
//  
//  
//  
//  Wire.write("Mooo- "); // respond with message of 6 bytes
//  // as expected by master
//
// 
//}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  //
  // protocol
  // Start    1,"buff",0
  // Stop     2,"",0
  // Reset    3,"",0
  // Message  4,"String,0/1
  // Sensor   5,"T/F",x
  // Temp     6,"",X
  // Speed    7,"",X 
 
  String InBuff="";
  //
  // Get command Identifier 00 to 15
  int Command = Wire.read();
  if (Command >0 and Command<20) {

    while(2 < Wire.available()) 	// loop through all but the last 3
    {
      char c = Wire.read(); 	     // receive byte as a character
      InBuff+=c;
      //if (c==","){InBuff="";} 
      //else if() {In+=c}
      //Command+=c;               // build command
      //Serial.print(c);         	// print the character
    }
    //
    //Wire.read();        // read string terminator
    //Wire.read();        // read a seperator   
    //int InVal1 = Wire.read();        // read string terminator
    //int InVal2 = Wire.read();        // read a seperator 
    int InVal2 = Wire.read();     // read last char
    int InVal3 = Wire.read();     // read last char

    int InVal= (InVal3 << 8)+InVal2;
    //
    // if this worked i should have all parts
    //Serial.println("Incomming bytes:" + (String) howMany);
    //
    // if display is not read skip the incomming update
    if (DisplayReady==true)
    {  
      //if (Dev==true) {Serial.println("Command:" + (String) Command +" Val:"+(String)InVal);};
      //if (Dev==true) {Serial.println("Command:" + (String) Command +" Val:"+(String)InVal2+" Val:"+(String)InVal3);};
      switch (Command) {
        case 1: // Start timer
          //RunTime=0;
          RunTime=millis();
          SystemRun=true;
          break;
        
        case 2: // Stop timer
          //RunTime=0;
          SystemRun=false;      
          break;
          
        case 3: // Reset all info
          RunTime=0;              // Resets Clock on Display          
          SystemRun=true;         // forces clock update to 00:00
          DisplayTemperature(0);  // Reset Temperature until next update 
          DisplayClear();         //
          UpdateSensor(0,false);  // Oil Pressure
          UpdateSensor(1,false);  // Normal Speed
          UpdateSensor(2,false);  // Over Speed
          UpdateSensor(3,true);   // Centrifuge
          UpdateSensor(4,false);  // Boost Pump               
          break;
          
        case 4: // Display Message
          if (InBuff.length() >=4 and InBuff.length() <=20) 
            DisplayMessage(InBuff);     
            break;
          
        // case 5: //DisplayMessage(InBuff);     
        //    if (InBuff=="true") 
        //      {UpdateSensor(InVal,true);}
        //    else
        //      {UpdateSensor(InVal,false);}
        //   break;
        
        case 6: // Temperature
          DisplayTemperature(InVal);  
          break;    
          
        case 7: // Speed Not Used Yet
          DisplaySpeed(InVal);       
          break;     

        // case 8:
        // case 9:
        
        case 10: // Sensor 0 Overspeed
          if (InVal==0) 
            {UpdateSensor(0,true);}
          else
            {UpdateSensor(0,false);}
          break;
        case 11: // Sensor 1  Norm RPM
          if (InVal==0) 
            {UpdateSensor(1,true);}
          else
            {UpdateSensor(1,false);}
          break;
        case 12: // Sensor 2 Oil
          if (InVal==0) 
            {UpdateSensor(2,true);}
          else
            {UpdateSensor(2,false);}
          break;
        case 13: // Sensor 3 centrifuge
          if (InVal==0) 
            {UpdateSensor(3,true);}
          else
            {UpdateSensor(3,false);}
          break;
        case 14: // Sensor 4 pump
          if (InVal==0) 
            {UpdateSensor(4,true);}
          else
            {UpdateSensor(4,false);}
          break;
        
          
        default:
          // Nothing to do      
          break;
      }
    }
  }
}
//
// Right hand Pressure sensors
void UpdateSensor(int SwitchNo,bool Sensor)
{  
  if (Sensor== true) 
    {my_lcd.Fill_Rect( 432,(SwitchNo*25)+12,27,17, RED );}
  else
    {my_lcd.Fill_Rect( 432,(SwitchNo*25)+12,27,17, BLACK );}
}
//
// Poss RPM Sensor display
void DisplaySpeed(int NewSpeed)
  {
    // ToDo
  }
//
// Temperature Pointer display
void DisplayTemperature(int NewTemp)
{
  //
  if (Dev==true) {Serial.println("Temp:" + (String)NewTemp);};
  //Temp Range 0-1000
  NewTemp=((NewTemp/3.33)-300)*-1;
  if (DisplayReady==true) 
  {
    //
    DisplayReady=false;      
    //
    
    //  
    if (TempPointer != NewTemp) {
      // Clear Old
      my_lcd.Set_Draw_color(BLACK);
      my_lcd.Draw_Line((20*sin(TempPointer*Rad ))+365,(20*cos(TempPointer*Rad))+230,(45*sin(TempPointer*Rad))+365,(45*cos(TempPointer*Rad))+230);  
      my_lcd.Draw_Line((20*sin((TempPointer-1)*Rad ))+365,(20*cos((TempPointer-1)*Rad))+230,(45*sin((TempPointer-1)*Rad))+365,(45*cos((TempPointer-1)*Rad))+230); 

      //my_lcd.Fill_Triangle(0, y0,  x1,  y1,  x2, y2)
      // Show New  
      my_lcd.Set_Draw_color(WHITE);
      my_lcd.Draw_Line((20*sin(NewTemp*Rad ))+365,(20*cos(NewTemp*Rad))+230,(45*sin(NewTemp*Rad))+365,(45*cos(NewTemp*Rad))+230);  
      my_lcd.Draw_Line((20*sin((NewTemp-1)*Rad ))+365,(20*cos((NewTemp-1)*Rad))+230,(45*sin((NewTemp-1)*Rad))+365,(45*cos((NewTemp-1)*Rad))+230);
      // Save Current Pointer
      TempPointer=NewTemp;
    }
    //
    DisplayReady=true;
  }
}

void DisplayClear()
{ // should we wait for DisplayReady ??

  //Line1="";Line2="";Line3="";
  //my_lcd.Set_Text_colour(BLACK);
  //my_lcd.Draw_Rectangle( 10,  170, 260, 300);  
  //my_lcd.Fill_Rect( 10,170,260,300, BLACK );
  DisplayMessage("");
  DisplayMessage("");
  DisplayMessage("");
  DisplayMessage("Reset");
  }

void DisplayMessage(String Message)
{   
  if (DisplayReady==true) 
  {
    //
    DisplayReady=false;
  
    my_lcd.Set_Text_Size(2);
  
    my_lcd.Set_Text_colour(BLACK);
    my_lcd.Print_String(Line1, 15, 192);         
    my_lcd.Set_Text_colour(WHITE);
    my_lcd.Print_String(Line2, 15, 192);      
        
    my_lcd.Set_Text_colour(BLACK);
    my_lcd.Print_String(Line2, 15, 212);      
    my_lcd.Set_Text_colour(WHITE);
    my_lcd.Print_String(Line3, 15, 212);      
    
    my_lcd.Set_Text_colour(BLACK);
    my_lcd.Print_String(Line3, 15, 232);        
    my_lcd.Set_Text_colour(WHITE);
    my_lcd.Print_String(Line4, 15, 232);        
  
    my_lcd.Set_Text_colour(BLACK);
    my_lcd.Print_String(Line4, 15, 252);        
    my_lcd.Set_Text_colour(WHITE);   
    my_lcd.Print_String(Message, 15, 252);      
    //
    // my_lcd.Set_Text_colour(BLACK);
    // my_lcd.Print_String(Line5, 15, 282);        
    // my_lcd.Set_Text_colour(WHITE);   
    // my_lcd.Print_String(Message,15, 282);      

    Line1=Line2;
    Line2=Line3;
    Line3=Line4;
    Line4=Message;//Line5;
    //Line5=Message;
    if (Dev==true) {Serial.println(Message);};
  
    DisplayReady=true;
  }
}

void DisplayTime()
  {
  int TxtColour = 0xE000;
  String Out ="";   

  if (SystemRun==true) //or (SystemRun==false and RunTime>0))
  //if (DisplayReady==true) 
  {
    //
    //DisplayReady=false;
    //
    if (RunTime ==0)
      {
        Out ="00:00";
        //my_lcd.Set_Text_colour(0xE000);   
        SystemRun=false;
      }
      
    else
      {
        Out="";
        Ts=((millis()-RunTime)/1000);
        Tm=(Ts/60);  
        Tm= Tm-((Tm/60)*60);
        
        if (Tm>=60) Tm=0;
        if (Tm<10) Out ="0";
        Out+=(String)Tm +":";  
        
        Ts= Ts-((Ts/60)*60);            
        if (Ts<10) Out +="0";
        Out+=(String)Ts;  
        TxtColour =WHITE;        
      } 
    //
      my_lcd.Set_Text_colour(TxtColour);    
      my_lcd.Set_Text_Size(5);        
      my_lcd.Print_String(Out , 30, 55);         
      //if (Dev==true) {Serial.println("time :" + Out);};
    } 
   //    
  }
//
//
//void windowScroll(int16_t x, int16_t y, int16_t wid, int16_t ht, int16_t dx, int16_t dy, uint16_t *buf)
//{
//    if (dx)
//    { 
//      for (int16_t row = 0; row < ht; row++) 
//      {
//            my_lcd.Read_GRAM(x, y + row, buf,wid, 1);
//            my_lcd.Set_Addr_Window(x, y + row, x + wid - 1, y + row);
//            my_lcd.Push_Any_Color(buf + dx, wid - dx, 1,0);
//            my_lcd.Push_Any_Color(buf + 0, dx, 0,0);
//        }
//    }
//    if (dy) 
//    {
//      for (int16_t col = 0; col < wid; col++) 
//      {
//            my_lcd.Read_GRAM(x + col, y, buf,1, ht);
//            my_lcd.Set_Addr_Window(x + col, y, x + col, y + ht - 1);
//            my_lcd.Push_Any_Color(buf + dy, ht - dy, 1,0);
//            my_lcd.Push_Any_Color(buf + 0, dy, 0,0);
//      }
//    }
//}
//
//
//draw some filled triangles
 void fill_triangles_test(void)
{
   int y0 = 0;
   int y2 = 0;


   int i = 0;
   my_lcd.Fill_Screen(BLACK);
    for(i=my_lcd.Get_Display_Width()/2-1;i>0;i-=5)
   {
    
      my_lcd.Set_Draw_color(0,i+64,i+64);
      my_lcd.Fill_Triangle(my_lcd.Get_Display_Width()/2-1,my_lcd.Get_Display_Height()/2-1-i,
                    my_lcd.Get_Display_Width()/2-1-i,my_lcd.Get_Display_Height()/2-1+i,
                    my_lcd.Get_Display_Width()/2-1+i,my_lcd.Get_Display_Height()/2-1+i);                   
      my_lcd.Set_Draw_color(i,0,i);
      my_lcd.Draw_Triangle(my_lcd.Get_Display_Width()/2-1,my_lcd.Get_Display_Height()/2-1-i,
                    my_lcd.Get_Display_Width()/2-1-i,my_lcd.Get_Display_Height()/2-1+i,
                    my_lcd.Get_Display_Width()/2-1+i,my_lcd.Get_Display_Height()/2-1+i);                   
   }
}

Credits

dbeacroft
0 projects • 3 followers

Comments