Steve Massikker
Published © Apache-2.0

Railway Interlocking System

Create massive train traffic on your own model railway!

IntermediateShowcase (no instructions)18,611
Railway Interlocking System

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×2
Generic Motor-driver L298
×2
Free URB unit
You need to send GERBER file to your PCB manufacturer and assemble the unit yourself
×2
Generic Arduino sensor modules (Hall or IR)
×7
5 mm LED: Red
5 mm LED: Red
or railway two-lenses signal
×6
5 mm LED: Green
5 mm LED: Green
×4
Resistor 221 ohm
Resistor 221 ohm
×5
Resistor 10k ohm
Resistor 10k ohm
×2

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Track Plan

Circuit of Interlocking system

Code

COMM unit sketch

Arduino
// -------------------------------------------------- //
// WWW.ARDUINORAILWAYCONTROL.COM                      //
// comm_urb.ino                                       //
// V.2.0  11/24/2019                                  //
//                                                    //
// SMALL INTERLOCKING                                 //
// For URB unit V.2.FINAL                             //
// 122Hz Thrust contol                                //
// https://arduinorailwaycontrol.com/urb_unit.html    //
//                                                    //
// Author: Steve Massikker                            //
// -------------------------------------------------- //
void(* resetFunc) (void) = 0;

#include <Wire.h>
#include <SoftwareSerial.h>

// Bluetooth module
SoftwareSerial Bluetooth(12, 13); // D12 - RX | D13 - TX

// PWM (SPEED)
#define MD_1_ENA 10 // LINES A, B
#define MD_1_ENB 9  // BLOCK 1
#define MD_2_ENA 11 // BLOCK 3
#define MD_2_ENB 3  // BLOCK 2
// DIRECTION DRIVER A 
#define MD_1_IN1 4  
#define MD_1_IN2 2
// DIRECTION DRIVER B
#define MD_12_IN3_IN3_IN1 5 
#define MD_12_IN4_IN4_IN2 6
// SENSORS
#define SNS_1 17
#define SNS_2 14
#define SNS_3 15
#define SNS_4 7
#define SNS_5 8
#define SNS_6 16
#define SNS_7 A6

// Variables
int addressI2C;
byte dataToI2C;
bool stringComplete = false;
String inputString = "";

  // 24 speed
  byte speedArrayA [] = {30,40,50,60,80,110,140,160,180,200,220,255};
  byte speedTrainA = 0;

  // Interlocking
  unsigned long timerBlock1, timerBlock2, timerBlock3, timerStation;
  byte speedBlock1, speedBlock2, speedBlock3; 
  
  // Dispather AWS
  bool flag_interlocking = false;  
  bool flag_station_open = true;    

  // Sensors
  unsigned long timer_protected_sensor1, timer_protected_sensor3,
                timer_protected_sensor5, timer_protected_sensor7;
  bool protected_sensor1, protected_sensor3, 
       protected_sensor5, protected_sensor7;
  bool sensor_block1_IN, sensor_block2_IN, sensor_block3_IN, 
       sensor_block1_OUT, sensor_block2_OUT, sensor_block3_OUT, 
       sensor_OUT;
  bool latch_stop_block1, latch_stop_block2, latch_stop_block3;
  
  // Latches     
  bool open_block1 = true, open_block2 = true, open_block3 = true;

  // TURNOUTS & SET DEFAULT POSITIONS
  bool switch_A = true, switch_B = false;
      

void setup() {

  // Initializie Serials & I2C
  Serial.begin(9600);
  Bluetooth.begin(9600);
  inputString.reserve(4);
  Wire.begin(); // Set as Master

  // Initialize pins
  pinMode (MD_1_ENA, OUTPUT);
  pinMode (MD_1_ENB, OUTPUT);
  pinMode (MD_2_ENA, OUTPUT);
  pinMode (MD_2_ENB, OUTPUT);  
  pinMode (MD_1_IN1, OUTPUT);
  pinMode (MD_1_IN2, OUTPUT);
  pinMode (MD_12_IN3_IN3_IN1, OUTPUT);
  pinMode (MD_12_IN4_IN4_IN2, OUTPUT);  
  pinMode (SNS_1, INPUT);
  pinMode (SNS_2, INPUT);
  pinMode (SNS_3, INPUT);
  pinMode (SNS_4, INPUT);
  pinMode (SNS_5, INPUT);
  pinMode (SNS_6, INPUT);
  pinMode (SNS_7, INPUT);  

  // Set PWM frequency for D3, D9, D10, D11
  // Timer 1 divisor to 256 for PWM frequency of 122.55 Hz (9, 10)
  TCCR1B = TCCR1B & B11111000 | B00000100;  
  // Timer 2 divisor to 256 for PWM frequency of 122.55 Hz (3, 11)
  TCCR2B = TCCR2B & B11111000 | B00000100; 

  // Set default direction to FORWARD (POLARITY)  NON Interlocking
  digitalWrite(MD_1_IN1, LOW);
  digitalWrite(MD_1_IN2, HIGH); 
  digitalWrite(MD_12_IN3_IN3_IN1, LOW);
  digitalWrite(MD_12_IN4_IN4_IN2, HIGH); 
 
}

void loop() {

  // ----  START PARSING INCOMING APP COMMANDS
  if (stringComplete) {
    // RESET 
    if (inputString =="999z") {
      dataToI2C = 99;
      addressI2C = 2;      
      sendDataViaI2C();
      resetFunc();
    }

    // FUNCTIONS 
    if (inputString.charAt(0) =='a') {
        // Speed   
      if (inputString.charAt(1) =='0') {
        if (inputString.charAt(2) =='0') speedTrainA = 0;
        if (inputString.charAt(2) =='2') speedTrainA = speedArrayA[0];
        if (inputString.charAt(2) =='4') speedTrainA = speedArrayA[1];
        if (inputString.charAt(2) =='6') speedTrainA = speedArrayA[2];
        if (inputString.charAt(2) =='8') speedTrainA = speedArrayA[3]; 
      } 
      if (inputString.charAt(1) =='1') {
        if (inputString.charAt(2) =='0') speedTrainA = speedArrayA[4];
        if (inputString.charAt(2) =='2') speedTrainA = speedArrayA[5];
        if (inputString.charAt(2) =='4') speedTrainA = speedArrayA[6];
        if (inputString.charAt(2) =='6') speedTrainA = speedArrayA[7];
        if (inputString.charAt(2) =='8') speedTrainA = speedArrayA[8];
      }
      if (inputString.charAt(1) =='2') {
        if (inputString.charAt(2) =='0') speedTrainA = speedArrayA[9];
        if (inputString.charAt(2) =='2') speedTrainA = speedArrayA[10];
        if (inputString.charAt(2) =='4') speedTrainA = speedArrayA[11];         
      }

      // Direction and Stop
      if (inputString.charAt(1) =='d') {
        if (inputString.charAt(2) =='f') { // (f) Forward
          digitalWrite(MD_1_IN1, LOW);
          digitalWrite(MD_1_IN2, HIGH); 
          digitalWrite(MD_12_IN3_IN3_IN1, LOW);
          digitalWrite(MD_12_IN4_IN4_IN2, HIGH); 
        }
        if (inputString.charAt(2) =='b') { // (b) Backward - Interlocking
          digitalWrite(MD_1_IN1, HIGH);
          digitalWrite(MD_1_IN2, LOW); 
          digitalWrite(MD_12_IN3_IN3_IN1, HIGH); 
          digitalWrite(MD_12_IN4_IN4_IN2, LOW); 
        }
        if (inputString.charAt(2) =='s') { // (s) Stop button
          speedTrainA = 0;
        } 

        analogWrite(MD_1_ENA, speedTrainA); // Throttle Driver A
        
        if (!flag_interlocking) {
          analogWrite(MD_1_ENB, speedTrainA);
          analogWrite(MD_2_ENB, speedTrainA);
          if (flag_station_open) analogWrite(MD_2_ENA, speedTrainA); 
          else analogWrite(MD_2_ENA,0); 
        }    
      }
    }  

    if (inputString.charAt(0) =='j') {
      // Switch A
      if (inputString.charAt(1) =='a') { 
        if (inputString.charAt(2) =='0') { // Branch direction
          switch_A = false;
          Bluetooth.print("a0z"); // Feedback to App
          Serial.print("a0z");
          addressI2C = 2; dataToI2C = 30; sendDataViaI2C();
        }
        if (inputString.charAt(2) =='1') { // Throw direction
          switch_A = true;
          Bluetooth.print("a1z"); // Feedback to App
          Serial.print("a1z");      
          addressI2C = 2; dataToI2C = 31; sendDataViaI2C();
        } 
      }

      // Switch B
      if (inputString.charAt(1) =='b') { 
        if (inputString.charAt(2) =='0') {
          switch_B = false;
          Bluetooth.print("b0z"); 
          Serial.print("b0z");
          addressI2C = 2; dataToI2C = 32; sendDataViaI2C();
        }
        if (inputString.charAt(2) =='1') {
          switch_B = true;
          Bluetooth.print("b1z");
          Serial.print("b1z");      
          addressI2C = 2; dataToI2C = 33; sendDataViaI2C();
        } 
      }
    }
    
    // AUTOMATE ON | OFF
    if (inputString.charAt(0) =='f') {
      if (inputString.charAt(1) =='a') {

        if (inputString.charAt(2) =='0') {
          flag_interlocking = false;
          speedBlock1 = 0; speedBlock2 = 0; speedBlock3 = 0;
          addressI2C = 2; dataToI2C = 50; sendDataViaI2C(); // AUTOMATE OFF
        }
        if (inputString.charAt(2) =='1') {
          flag_interlocking = true;
          speedBlock1 = 180; speedBlock2 = 140; speedBlock3 = 160;
          digitalWrite(MD_1_IN1, HIGH);
          digitalWrite(MD_1_IN2, LOW); 
          digitalWrite(MD_12_IN3_IN3_IN1, HIGH); 
          digitalWrite(MD_12_IN4_IN4_IN2, LOW); 
          protected_sensor1 = false; protected_sensor3 = false;
          protected_sensor5 = false; protected_sensor7 = false;
          latch_stop_block1 = false; latch_stop_block2 = false; latch_stop_block3 = false;
          open_block1 = true; open_block2 = true; open_block3 = true;          
          addressI2C = 2; dataToI2C = 51; sendDataViaI2C(); // AUTOMATE ON
        }

        analogWrite(MD_1_ENB, speedBlock1);
        analogWrite(MD_2_ENB, speedBlock2);
        analogWrite(MD_2_ENA, speedBlock3); 
      }  
    }

    dataToI2C = 0;
    inputString = "";
    stringComplete = false;
  }

  // ----  MAIN BLOCK
  if (flag_interlocking) {

    // CHECK SENSORS STATES

    if (digitalRead(SNS_1) == LOW) { // BLOCK 1 | HALL
      sensor_block1_IN = true;
      open_block1 = false;
      //Serial.println("SENSOR 1");
    }
    else sensor_block1_IN = false; 
    if (digitalRead(SNS_2) == LOW) { // HALL
      sensor_block1_OUT = true;
      latch_stop_block1 = true;
      //Serial.println("SENSOR 2");    
    }
    else sensor_block1_OUT = false;
  
    if (digitalRead(SNS_3) == HIGH) { // BLOCK 2 | IR
      sensor_block2_IN = true;
      open_block2 = false;
      //Serial.println("SENSOR 3");    
    }
    else sensor_block2_IN = false; 
    if (digitalRead(SNS_4) == LOW) { // HALL
      sensor_block2_OUT = true;
      latch_stop_block2 = true;
      //Serial.println("SENSOR 4");    
    }
    else sensor_block2_OUT = false;  
    
    if (digitalRead(SNS_5) == HIGH) { // BLOCK 3 | IR
      sensor_block3_IN = true;
      open_block3 = false;
      //Serial.println("SENSOR 5");    
    }
    else sensor_block3_IN = false;
    if (digitalRead(SNS_6) == LOW) { // HALL
      sensor_block3_OUT = true;  
      latch_stop_block3 = true;
      //Serial.println("SENSOR 6");    
    }
    else sensor_block3_OUT = false;  
    
    // OUT
    if (analogRead(SNS_7) > 550) { // ANALOGUE HALL
      sensor_OUT = true;
      //Serial.println("SENSOR 7");
    }      
    else sensor_OUT = false;   
  
    // RESET BLOCK LATCHES
    if (sensor_block2_IN) open_block1 = true; 
    if (sensor_block3_IN) open_block2 = true;
    if (sensor_OUT) open_block3 = true;

    // RESET AUTOSTOP LATCHES
    if (sensor_block2_IN) latch_stop_block1 = false;
    if (sensor_block3_IN) latch_stop_block2 = false;        
    if (sensor_OUT) latch_stop_block3 = false;
    
    // ----  SIGNALS
    // SIGNAL 1
    if (sensor_block1_IN && !protected_sensor1) {
      timer_protected_sensor1 = millis();
      protected_sensor1 = true; 
      addressI2C = 2; dataToI2C = 101; sendDataViaI2C();
    }  
    // SIGNAL 2
    if (sensor_block2_IN && !protected_sensor3) {
      timer_protected_sensor3 = millis();
      protected_sensor3 = true;      
      addressI2C = 2; dataToI2C = 102; sendDataViaI2C();
    }    
    // SIGNAL 3
    if (sensor_block3_IN && !protected_sensor5) {
      timer_protected_sensor5 = millis();
      protected_sensor5 = true;       
      addressI2C = 2; dataToI2C = 103; sendDataViaI2C();
    } 
    // SIGNAL 4 
    if (sensor_OUT && !protected_sensor7) {
      timer_protected_sensor7 = millis();
      timerStation = millis();
      protected_sensor7 = true; 
      flag_station_open = false;      
      addressI2C = 2; dataToI2C = 104; sendDataViaI2C();
    } 

  // AUTOSTOP
    // BLOCK 1
    if (!open_block2) {
      if (latch_stop_block1) {
        if (millis() > (timerBlock1 + 100)) {
          timerBlock1 = millis();
          if (speedBlock1 > 40) speedBlock1 = speedBlock1 - 25;
          else speedBlock1 = 0;
        }
      }  
    }
    else speedBlock1 = 180;

    // BLOCK 2
    if (!open_block3) {
      if (latch_stop_block2) {
        if (millis() > (timerBlock2 + 100)) {
          timerBlock2 = millis();
          if (speedBlock2 > 60) speedBlock2 = speedBlock2 - 30;
          else speedBlock2 = 0;
        }
      }  
    }
    else speedBlock2 = 140;

    // BLOCK 3
    if (!flag_station_open) {
      if (latch_stop_block3) {
        if (millis() > (timerBlock3 + 100)) {
          timerBlock3 = millis();
          if (speedBlock3 > 40) speedBlock3 = speedBlock3 - 18;
          else speedBlock3 = 0;
        }
      } 
    }
    else speedBlock3 = 160;
    
    analogWrite(MD_1_ENB, speedBlock1);
    analogWrite(MD_2_ENB, speedBlock2);
    analogWrite(MD_2_ENA, speedBlock3);     
  }

  // ----  TIMERS
  if (millis() > (timer_protected_sensor1 + 1000)) protected_sensor1 = false;
  if (millis() > (timer_protected_sensor3 + 1500)) protected_sensor3 = false;
  if (millis() > (timer_protected_sensor5 + 1500)) protected_sensor5 = false;
  if (millis() > (timer_protected_sensor7 + 1000)) protected_sensor7 = false;
  if (millis() > (timerStation + 15000)) flag_station_open = true; // Delay 15 sec.

  bluetoothEvent();

}  

// ----------- FUNCTIONS ----------- //
void serialEvent() {
  if (Serial.available()) {
    char inChar = (char)Serial.read();
    inputString += inChar;
    if (inChar == 'z') {
      stringComplete = true;
    }
  }
}

void bluetoothEvent() {
  if (Bluetooth.available()) {
    char inChar = (char)Bluetooth.read();
    inputString += inChar;
    if (inChar == 'z') {
      //Serial.println(inputString); // Command from App
      stringComplete = true;
    }
  }
}

void sendDataViaI2C() {
  Wire.beginTransmission(addressI2C);
  Wire.write(dataToI2C);
  Wire.endTransmission();
}

LOCAL unit sketch

Arduino
// -------------------------------------------------- //
// WWW.ARDUINORAILWAYCONTROL.COM                      //
// local_urb.ino                                      //
// V.2.0  11/24/2019                                  //
//                                                    //
// SMALL INTERLOCKING                                 //
// For URB unit V.2.FINAL                             //
// https://arduinorailwaycontrol.com/urb_unit.html    //
//                                                    //
// Author: Steve Massikker                            //
// -------------------------------------------------- //
void(* resetFunc) (void) = 0;

#include <Wire.h> 
#include <Servo.h>

// SERVO
#define JUNCTION_EN 4
Servo J1;  // TURNOUT A
Servo J2;  // TURNOUT B

// SIGNALS
#define S_LA_RED 3 // SIGNAL LINE A
#define S_LB_RED 2 // SIGNAL LINE B
#define S_B3_RED 6 // SIGNAL BLOCK 3
#define S_B3_GRN 5
#define S_OUT_RED 10 // SIGNAL OUT
#define S_OUT_GRN 9
#define S_B1_RED 17 // SIGNAL BLOCK 1
#define S_B1_GRN 16
#define S_B2_RED 15 // SIGNAL BLOCK 2
#define S_B2_GRN 14


// RELAY
#define RELAY_LA 13 //  LINE A
#define RELAY_LB 12 //  LINE B

// Variables
byte dataFromI2C;
unsigned long millisJunctions, timerStation;
bool switch_A = true, 
     switch_B = false,
     flag_blink_AB = false,
     flag_change_junc = false;

bool flag_interlocking = false,
     flag_station_open = true;
     
bool open_block1 = true, 
     open_block2 = true,
     open_block3 = true;

void setup() {

  // Initializie Hardware Serial & I2C
  Serial.begin(9600);
  Wire.begin(2); // Set address #2
  Wire.onReceive(receiveI2C);

  // Initialize pins  
  pinMode (JUNCTION_EN, OUTPUT);
  pinMode (RELAY_LA, OUTPUT);
  pinMode (RELAY_LB, OUTPUT);  
  pinMode (S_LA_RED, OUTPUT);
  pinMode (S_LB_RED, OUTPUT);
  pinMode (S_B3_GRN, OUTPUT);
  pinMode (S_B3_RED, OUTPUT);
  pinMode (S_B1_RED, OUTPUT);
  pinMode (S_B1_GRN, OUTPUT);  
  pinMode (S_B2_RED, OUTPUT);
  pinMode (S_B2_GRN, OUTPUT);
  pinMode (S_OUT_RED, OUTPUT);
  pinMode (S_OUT_GRN, OUTPUT); 

  // Initialize Servos
  J1.attach(7);
  J2.attach(8);

  // Set default
  J1.write(180); J2.write(0);
  delay(900);
  digitalWrite(JUNCTION_EN, LOW);
  digitalWrite(S_LA_RED, LOW); 
  digitalWrite(S_LB_RED, HIGH);   
  digitalWrite(S_B1_GRN, LOW);
  digitalWrite(S_B1_RED, HIGH);
  digitalWrite(S_B2_GRN, LOW);
  digitalWrite(S_B2_RED, HIGH);
  digitalWrite(S_B3_GRN, LOW);
  digitalWrite(S_B3_RED, HIGH);
  digitalWrite(S_OUT_GRN, HIGH);    
  digitalWrite(S_OUT_RED, LOW);
  digitalWrite(RELAY_LA, LOW); // Line A is ON 
  digitalWrite(RELAY_LB, HIGH); // Line B is OFF
}

void loop() {

// ---- COMMAND PARSING
  if (dataFromI2C != 0) {
    
    switch (dataFromI2C) {

      // RESET
      case 99: resetFunc(); break;

      // SWITCH A
      case 30: J1.write(0);
           delay(50);
           switch_A = false;
           flag_change_junc = true;
           digitalWrite(JUNCTION_EN, HIGH);
           Serial.println("SWITCH A Branch");
           millisJunctions = millis();
           break;

      case 31: J1.write(180);
           delay(50);
           switch_A = true;
           flag_change_junc = true;         
           digitalWrite(JUNCTION_EN, HIGH);
           Serial.println("SWITCH A Straight");         
           millisJunctions = millis();
           break;

      // SWITCH B
      case 32: J2.write(0);
           delay(50);
           switch_B = false; 
           flag_change_junc = true;             
           digitalWrite(JUNCTION_EN, HIGH);
           Serial.println("SWITCH B Branch");
           millisJunctions = millis();
           break;

      case 33: J2.write(180);
           delay(50);
           switch_B = true; 
           flag_change_junc = true;             
           digitalWrite(JUNCTION_EN, HIGH);
           Serial.println("SWITCH B Straight");
           millisJunctions= millis();
           break;

      // MODE
      case 50: flag_interlocking = false;  
           break;
      case 51: flag_interlocking = true;  
           break; 

      // BLOCKS STATE
      case 101: open_block1 = false;
           Serial.println("BLOCK 1");
           break;
      case 102: open_block2 = false;
           open_block1 = true; 
           Serial.println("BLOCK 2");           
           break;
      case 103: open_block3 = false;
           open_block2 = true;
           Serial.println("BLOCK 3");
           break;
      case 104: open_block3 = true;
           flag_station_open = false;
           timerStation = millis();
           Serial.println("OUT");
           break;
    }
     
    dataFromI2C = 0;
  }

// ---- SIGNALS
  // BLOCK 1
  if (flag_interlocking) {
    if (open_block1) {
      digitalWrite(S_B1_GRN, HIGH); 
      digitalWrite(S_B1_RED, LOW);     
    }
    else {
      digitalWrite(S_B1_GRN, LOW); 
      digitalWrite(S_B1_RED, HIGH); 
    }
  // BLOCK 2
    if (open_block2) {
      digitalWrite(S_B2_GRN, HIGH); 
      digitalWrite(S_B2_RED, LOW);     
    }
    else {
      digitalWrite(S_B2_GRN, LOW); 
      digitalWrite(S_B2_RED, HIGH); 
    }
  // BLOCK 3
    if (open_block3) {
      digitalWrite(S_B3_GRN, HIGH); 
      digitalWrite(S_B3_RED, LOW);     
    }
    else {
      digitalWrite(S_B3_GRN, LOW); 
      digitalWrite(S_B3_RED, HIGH); 
    }
  }
  else {
    digitalWrite(S_B1_GRN, LOW);
    digitalWrite(S_B1_RED, HIGH);
    digitalWrite(S_B2_GRN, LOW);
    digitalWrite(S_B2_RED, HIGH);
    digitalWrite(S_B3_GRN, LOW);
    digitalWrite(S_B3_RED, HIGH);
  }   

  // OUT
  if (flag_station_open) {
    digitalWrite(S_OUT_GRN, HIGH);    
    digitalWrite(S_OUT_RED, LOW);
  }
  else {
    digitalWrite(S_OUT_GRN, LOW);    
    digitalWrite(S_OUT_RED, HIGH);
  }

// ---- RELAY
  if (flag_change_junc) {

  	if (switch_A && !switch_B) { // Line A
      digitalWrite(S_LA_RED, LOW);
      digitalWrite(S_LB_RED, HIGH); // RED     
      digitalWrite(RELAY_LA, LOW);
      digitalWrite(RELAY_LB, HIGH); 
      flag_blink_AB = false;
  	}
  	else if (!switch_A && switch_B) { // Line B
      digitalWrite(S_LA_RED, HIGH); // RED
      digitalWrite(S_LB_RED, LOW);       
      digitalWrite(RELAY_LA, HIGH); 
      digitalWrite(RELAY_LB, LOW); 
      flag_blink_AB = false;     
  	}
  	else { // Undefined
      digitalWrite(RELAY_LA, HIGH); 
      digitalWrite(RELAY_LB, HIGH); 
      flag_blink_AB = true;     
  	}
 
    flag_change_junc = false; 
  } 

  if (millis() > (millisJunctions + 900)) digitalWrite(JUNCTION_EN, LOW);
  if (millis() > (timerStation + 15000)) flag_station_open = true; // Delay 15 sec.  

  if (flag_blink_AB) {
  	static bool tick_blink;
    static unsigned long blinkMillis;
    if (millis() > (blinkMillis + 500)) { // 500 - blinking optimal period
      tick_blink = !tick_blink;
      blinkMillis = millis();
    }
    digitalWrite(S_LA_RED, tick_blink);
    digitalWrite(S_LB_RED, !tick_blink);
  }

}

// ----------- FUNCTIONS ----------- //

void receiveI2C(int howMany) {
  while (Wire.available() > 0) {
    dataFromI2C = Wire.read();
  }
}

Credits

Steve Massikker

Steve Massikker

6 projects • 166 followers
I'm Steve Massikker, a graphic and visual designer and former engineer of civil aviation.

Comments