#include <Servo.h> 
Servo esc1;
Servo esc2;
Servo esc3;
Servo esc4;

#define SRC_NEUTRAL 1504
#define SRC_MAX 2004
#define SRC_MIN 988
#define TRC_NEUTRAL 1504
#define TRC_MAX 1992
#define TRC_MIN 1004
#define SRC_INTERVAL 50
#define TRC_INTERVAL 30

uint16_t unSteeringMin = SRC_MIN;
uint16_t unSteeringMax = SRC_MAX;
uint16_t unSteeringCenter = SRC_NEUTRAL;

uint16_t unThrottleMin = TRC_MIN;
uint16_t unThrottleMax = TRC_MAX;
uint16_t unThrottleCenter = TRC_NEUTRAL;

#define pos_min 39
#define pos_max 140

#define PPM_SPEED_1 5
#define PPM_SPEED_2 6
#define PPM_SPEED_3 9
#define PPM_SPEED_4 10

#define THROTTLE_IN_PIN 2
#define STEERING_IN_PIN 3

#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
volatile uint8_t bUpdateFlagsShared;

volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;

uint32_t ulThrottleStart;
uint32_t ulSteeringStart;

uint8_t gThrottle = 0;
uint8_t gGear = 1;
uint8_t gOldGear = 1;

#define DIRECTION_STOP 0
#define DIRECTION_FORWARD 1
#define DIRECTION_REVERSE 2
#define DIRECTION_ROTATE_RIGHT 3
#define DIRECTION_ROTATE_LEFT 4

uint8_t gThrottleDirection = DIRECTION_STOP;
uint8_t gDirection = DIRECTION_STOP;
uint8_t gOldDirection = DIRECTION_STOP;

unsigned long pulse_time ;

void setup() { 
 Serial.begin(9600);
 attachInterrupt(0, calcThrottle, CHANGE);
 attachInterrupt(1, calcSteering, CHANGE);

 esc1.attach(PPM_SPEED_1);
 esc2.attach(PPM_SPEED_2);
 esc3.attach(PPM_SPEED_3);
 esc4.attach(PPM_SPEED_4);
 
 pulse_time = millis();

arm();
}

void arm() 
{
 Serial.println("Arming start");
 for(int i = 0; i < 50; i++)
 {
 esc1.write(140);
 esc2.write(140);
 esc3.write(140);
 esc4.write(140);
 delay(1);
 esc1.write(39);
 esc2.write(39);
 esc3.write(39);
 esc4.write(39);
 delay(20);
 }
 Serial.println("Arming done");
}

void loop() {
 static uint16_t unThrottleIn;
 static uint16_t unSteeringIn;
 static uint8_t bUpdateFlags;

 Serial.print("Throttle: ");
 Serial.println(unThrottleInShared);
 Serial.print("Steering: ");
 Serial.println(unSteeringInShared);

if(bUpdateFlagsShared)
 {
 noInterrupts();
 pulse_time = millis();
 bUpdateFlags = bUpdateFlagsShared;

if(bUpdateFlags & THROTTLE_FLAG)
 {
 unThrottleIn = unThrottleInShared;
 }
 if(bUpdateFlags & STEERING_FLAG)
 {
 unSteeringIn = unSteeringInShared;
 }
 interrupts();
 }
 
 if(bUpdateFlags & THROTTLE_FLAG)
 {
 unThrottleIn = constrain(unThrottleIn, unThrottleMin, unThrottleMax);
 if(unThrottleIn > (unThrottleCenter + TRC_INTERVAL))
 {
 gThrottle = map(unThrottleIn,(unThrottleCenter + TRC_INTERVAL),unThrottleMax,pos_min,pos_max);
 gThrottleDirection = DIRECTION_FORWARD;
 }
 else if (unThrottleIn < (unThrottleCenter - TRC_INTERVAL))
 {
 gThrottle = map(unThrottleIn,unThrottleMin,(unThrottleCenter-TRC_INTERVAL),pos_max,pos_min);
 gThrottleDirection = DIRECTION_REVERSE;
 }
 else
 {
 gThrottleDirection = DIRECTION_STOP;
 gThrottle=0;
 }
 }
 uint8_t throttleSlow = gThrottle;
 
 if(bUpdateFlags & STEERING_FLAG)
 {
 gDirection = gThrottleDirection;
 unSteeringIn = constrain(unSteeringIn, unSteeringMin, unSteeringMax);
 
 if(unSteeringIn > (unSteeringCenter + SRC_INTERVAL))
 {
 gDirection = DIRECTION_ROTATE_RIGHT;
 throttleSlow = map(unSteeringIn, unSteeringCenter, unSteeringMax, gThrottle, gThrottle*0.8);
 }
 else if(unSteeringIn<(unSteeringCenter - SRC_INTERVAL))
 {
 gDirection = DIRECTION_ROTATE_LEFT;
 throttleSlow = map(unSteeringIn, unSteeringMin, unSteeringCenter, gThrottle*0.8, gThrottle);
 }
 }
 
 if (gThrottleDirection == DIRECTION_FORWARD)
 {
 if (gDirection == DIRECTION_ROTATE_RIGHT)
 {
 esc1.write(39);
 esc2.write(39);
 esc3.write(gThrottle);
 esc4.write(throttleSlow);
 }
 else if (gDirection == DIRECTION_ROTATE_LEFT)
 {
 esc1.write(39);
 esc2.write(39);
 esc3.write(throttleSlow);
 esc4.write(gThrottle);
 }
 else
 {
 esc1.write(39);
 esc2.write(39);
 esc3.write(gThrottle);
 esc4.write(gThrottle);
 }
 }
 else if (gThrottleDirection == DIRECTION_REVERSE)
 {
 if (gDirection == DIRECTION_ROTATE_RIGHT)
 {
 esc1.write(throttleSlow);
 esc2.write(gThrottle);
 esc3.write(39);
 esc4.write(39);
 }
 else if (gDirection == DIRECTION_ROTATE_LEFT)
 {
 esc1.write(gThrottle);
 esc2.write(throttleSlow);
 esc3.write(39);
 esc4.write(39);
 }
 else
 {
 esc1.write(gThrottle);
 esc2.write(gThrottle);
 esc3.write(39);
 esc4.write(39);
 }
 }
 else {
 esc1.write(39);
 esc2.write(39);
 esc3.write(39);
 esc4.write(39);
 }

bUpdateFlags = 0;
}

void calcThrottle()
{
 if(digitalRead(THROTTLE_IN_PIN) == HIGH)
 {
 ulThrottleStart = micros();
 }
 else
 {
 unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
 bUpdateFlagsShared |= THROTTLE_FLAG;
 }
}

void calcSteering()
{
 if(digitalRead(STEERING_IN_PIN) == HIGH)
 {
 ulSteeringStart = micros();
 }
 else
 {
 unSteeringInShared = (uint16_t)(micros() - ulSteeringStart);
 bUpdateFlagsShared |= STEERING_FLAG;
 }
}