Call light system with HC12

I'm building a call light system for my brother and his friends who do amateur car racing. I'm sorry if you head starts to spin figuring this out, but it's actually pretty simple.

The module for the pit and the one for the car are identical. Two buttons and two LED's. The first button is the pit button, the second is the acknowledge button. For the LED's, there's transmit and pit.

So let's say the pit wants to tell the car to come in. They press the pit button. Their transmit LED will start to blink and send out the message. The module in the car will receive the message, and transmit it back to the pit. (Note, the module sends back the message, not the driver.) When the pit module gets back the same message it sent, it knows the message got though, and now the transmit LED will turn on instead of blink. This is in case the car gets out of range on the other side of the track. It will transmit the same message every two seconds until it receives back the message it sent.

In the car, the pit LED will light up. The driver then presses the acknowledge button, which turns off the pit LED, and transmits back a message to the pit to turn off it's transmit light. One other function is that if one side hits the pit button, but then hits the acknowledge button, it cancels the call, but turning off the LED's.

I know that I'm transmitting data back and forth, but I've gotten all sorts of things going on with the LED's except what I want them to do. One side will go on, but not turn off, I change part of the sketch, now both LED's come on, I change something else, and still can't get it to work right.

The problem I'm having though is that the functions are kind of intertwined and I'm getting myself mixed up. I think I have it clear, until I sit down and try to tweak the sketch. So, I'm throwing in the towel, and asking for help. Below is my sketch, which is the same on both ends.

On a side note, I found that if I have any part of the sketch that would send a value of 0 over the HC12, the sketch gives me an error, so I'm avoiding using 0 for my value.

#include <SoftwareSerial.h>

SoftwareSerial HC12(2, 3); //TX pin and RX pin


byte val = 1; // 1 is no message, 2 is acknowledgement, 3 is go to pit
bool ack; //Acknowledge flag
byte rcv; // variabel to receive data
byte ackButtonState = 0; // when ack button pressed
byte transmitState = 0; //1 if transmitting, 0 if not
#define ackButton  4 // acknowledge button
#define pitButton  5 // Heading to pit button
#define goPitLED  6 // Go to pit LED
#define TxLED  7 // TxRx LED indicator

int toggleTxLEDState = 1;


unsigned long lastTimeAckChanged = millis ();
unsigned long lastTimePitChanged = millis ();
unsigned long debounceDelay = 50;
byte ackState = LOW;
byte pitState = LOW;

unsigned long lastTimePitBlinked = millis();
unsigned long lastTimeTxBlinked = millis();
unsigned long lastTimeTransmitted = millis();
unsigned long blinkDelayPit = 500;
unsigned long blinkDelayTx = 500;
unsigned long transmitDelayTx = 2000;
byte PitLEDState = LOW;
byte TxLEDState = LOW;

void setup() {
  Serial.begin (9600);
  HC12.begin (9600);
  pinMode(ackButton, INPUT); // Button 1
  pinMode(pitButton, INPUT); // Button 2
  pinMode(goPitLED, OUTPUT); // LED 1
  pinMode(TxLED, OUTPUT); // LED 2

  ackState = digitalRead(ackButton);
  pitState = digitalRead(pitButton);

}

void loop() {

  unsigned long timeNow = millis();

  // Transmit pit button
  if (timeNow - lastTimePitChanged > debounceDelay) {
    byte newPitButtonState = digitalRead(pitButton);
    if (newPitButtonState != pitState) {
      lastTimePitChanged = timeNow;
      pitState = newPitButtonState;
      if (pitState == HIGH) {
        val = 3;
        transmitState = 1;
      }
    }
  }
  // Transmit acknowledge
  if (timeNow - lastTimeAckChanged > debounceDelay) {
    byte newAckButtonState = digitalRead(ackButton);
    if (newAckButtonState != ackState) {
      lastTimeAckChanged = timeNow;
      ackState = newAckButtonState;
      if (ackState == HIGH) {
        val = 2;
      }
      if (transmitState == 1) {
        if (val == 3) {
          if (timeNow - lastTimeTxBlinked > blinkDelayTx) {
            lastTimeTxBlinked += blinkDelayTx;
            toggleTxLEDState = (toggleTxLEDState == HIGH) ? LOW : HIGH;  //Blinks Tx LED
            digitalWrite (TxLED, toggleTxLEDState);
          }
        }
        //Below will continute to transmit every 2 seconds as long as transmit state is 1
        if (timeNow - lastTimeTransmitted > transmitDelayTx) {
          lastTimeTransmitted += transmitDelayTx;
          HC12.write(val);
        }
        //Acknowledgment sent back:
        if (val == 2) {
          toggleTxLEDState = LOW;
          digitalWrite (TxLED, LOW);
          transmitState = 0;

        }
      }
      // Send acknowledgemnt and turn off go to pit LED
      if (transmitState == 0) {
        if (val == 2) {
          HC12.write (val);
          digitalWrite (goPitLED, LOW);
          val = 1;
        }
      }
      while (HC12.available()) {
        rcv = HC12.read();
        if ((transmitState == 0) && (rcv >= 3)) {//Receive message
          HC12.write (rcv);
          digitalWrite (goPitLED, HIGH); // turns on led
        }
        if ((rcv == val) && (transmitState == 1)) {//Receive verification message
          toggleTxLEDState = LOW;
          digitalWrite (TxLED, HIGH);//TxLED is the transmit LED.  Change to a variable that is set by the button push if more than one.
          transmitState = 0;
        }
        if (rcv == 2) {//Receiving the acknowledge from the receiver
          digitalWrite (TxLED, LOW);
          toggleTxLEDState = LOW;
          transmitState = 0;
          val = 1;
        }
      }
    }
  }
}

First question - how are your buttons wired up? You declare them as INPUT so you will need to have external pull-up/pull-down resistors to make them work.

As for the code, structure it more like a finite state machine. Google will provide plenty of reading material.

Another tip to improve readability. Rather than this

byte val = 1; // 1 is no message, 2 is acknowledgement, 3 is go to pit

use enumeration

enum { NO_MSG=1, ACK, GOTO_PIT } val;
//byte val = 1; // 1 is no message, 2 is acknowledgement, 3 is go to pit

then, in your code, you have human understandable constants, not random numbers

if (pitState == HIGH) {
        //val = 3;
        val = GOTO_PIT;
        transmitState = 1;
}