         continue;
 
       // Parse and reset
+      Serial.print(F("Handling command: "));
+      Serial.println(_bluetoothBuffer[0]);
       if (_bluetoothBuffer[0] == 'L')
         bluetoothLightCommand();
       else if (_bluetoothBuffer[0] == 'V')

typedef LightMode;


// Bluetooth declarations
SoftwareSerial _bluetoothSerial(2, 3);
const byte _bluetoothBufferSize = 42;
char _bluetoothBuffer[_bluetoothBufferSize];
byte _bluetoothBufferLength = 0;

// Lighting declarations
Adafruit_NeoPixel _lightStrip = Adafruit_NeoPixel(30, 6, NEO_GRB + NEO_KHZ800);
unsigned long _lightLoopLastTime = 0;
LightMode _lightMode = LightModeCycle;
uint32_t _lightSolidColour = 0xFF0000;
uint16_t _lightCycleCurrentOffsetTime = 0; // How many ms are we into the current sequence for when we are colour cycling
uint8_t _lightCycleColourCount = 4;
uint32_t _lightCycleColours[10];
uint32_t _lightCycleDuration = 10000; // In ms
uint32_t _lightCycleBlendAmount = _lightCycleDuration * 1; // How many different colours are visible of total amount at any time (i.e. 30 means each LED is a different colour within the cycle, 60 means the same but with every second colour skipped and 1 means everything is the same colour)

// Vibration declarations
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
unsigned long _lastMotorCycle = 0;
unsigned char _motorSequence = 0;


void setup() {
  // Begin serial for debugging purposes

  _lightStrip.show();
}

void vibrationPerformLoop() {  // Determine how long since we were last called - if we're within threshold then we do nothing
  uint64_t nowTime = millis();
  uint16_t timeSinceLast = (uint16_t)(nowTime - _vibrationLoopLastTime);
  if (timeSinceLast < 50) // Without this arbitrary delay the lower the delay is the more likely we get serial data corruption, the joys of hard-coded assembly
    return;
  _vibrationLoopLastTime = nowTime;

  // We need to determine where we are in the sequence by adding the ms offset and clamping it to total duration to cope with overflow
  _vibrationCycleCurrentOffsetTime += timeSinceLast;
  while (_vibrationCycleCurrentOffsetTime > _vibrationCycleDuration)
    _vibrationCycleCurrentOffsetTime -= _vibrationCycleDuration;
  
  // Now we enumerate through each motor's settings to determine where it currently is
  for (uint8_t pwmChannel = 0; pwmChannel < 9; pwmChannel++) {
    // Skip if there are no settings for this channel
    if (_vibrationSettings[pwmChannel].NumberValues < 1)
      continue;
    
    // Now based upon our current timestamp determine which value we should be using
    uint16_t startTime = 0;
    for (uint8_t valueIndex = 0; valueIndex < _vibrationSettings[pwmChannel].NumberValues; valueIndex++) {
      // If we have exceeded the end time for this value, simply update our start time and continue
      if (_vibrationCycleCurrentOffsetTime > _vibrationSettings[pwmChannel].Values[valueIndex].Duration + startTime) { 
        startTime += _vibrationSettings[pwmChannel].Values[valueIndex].Duration;
        continue;
      }
      
      // Determine how far through this segment we are
      float currentProgress = (float)(_vibrationCycleCurrentOffsetTime - startTime) / (float)_vibrationSettings[pwmChannel].Values[valueIndex].Duration;
      float diff = (int32_t)_vibrationSettings[pwmChannel].Values[valueIndex].End - (int32_t)_vibrationSettings[pwmChannel].Values[valueIndex].Start;
      uint16_t newValue = (uint16_t)(diff * currentProgress) + _vibrationSettings[pwmChannel].Values[valueIndex].Start;
      _pwmDriver.setPin(pwmChannel, newValue < 0 ? 0 : newValue > 4095 ? 4095 : newValue);
      
      // We've now set the value so we can break
      break;
    }
  }
}

void bluetoothParseInput() {
  // Return if no data to read
  uint32_t availableBytes = _bluetoothSerial.available();
