private void setDigitalInputs(int portNumber, int portData) {
    int portDifference = digitalInputData[portNumber] ^ portData;
    digitalInputData[portNumber] = portData;
    ArrayList<Integer> differentPinNumbers = new ArrayList();
    for (int i = 0; i < 8; i++) {
      if (BitsUtils.isBitSet((byte) portDifference, i)) differentPinNumbers.add(i);
    }

    for (int pinNumber : differentPinNumbers) {
      int actualPinNumber = (portNumber << 3) + pinNumber;
      if (isPinDebuggingEnabled)
        Log.d(
            "Device "
                + this.name
                + ": Pin #"
                + actualPinNumber
                + " status changed to "
                + (getDigitalPinStatus(actualPinNumber) ? "High" : "Low")
                + ".");
      for (OneSheeldDataCallback oneSheeldDataCallback : dataCallbacks) {
        oneSheeldDataCallback.onDigitalPinStatusChange(
            actualPinNumber, getDigitalPinStatus(actualPinNumber));
      }
    }
  }
  private void processInput(byte inputData) {
    byte command;
    if (parsingSysex) {
      if (inputData == END_SYSEX) {
        parsingSysex = false;
        byte sysexCommand = storedInputData[0];
        if (sysexBytesRead > 0) {
          byte[] sysexData = new byte[sysexBytesRead - 1];

          System.arraycopy(storedInputData, 1, sysexData, 0, sysexBytesRead - 1);

          byte[] fixedSysexData = null;
          if (sysexData.length % 2 == 0) {
            fixedSysexData = new byte[sysexData.length / 2];
            for (int i = 0; i < sysexData.length; i += 2) {
              fixedSysexData[i / 2] = (byte) (sysexData[i] | (sysexData[i + 1] << 7));
            }

            if (sysexCommand == SERIAL_DATA) {
              for (byte b : fixedSysexData) {
                serialBuffer.add(b);
                for (OneSheeldDataCallback oneSheeldDataCallback : dataCallbacks) {
                  oneSheeldDataCallback.onSerialDataReceive(b & 0xFF);
                }
              }
            } else if (sysexCommand == BLUETOOTH_RESET) {
              byte randomVal = (byte) (Math.random() * 255);
              byte complement = (byte) (255 - randomVal & 0xFF);
              synchronized (sendingDataLock) {
                sysex(BLUETOOTH_RESET, new byte[] {0x01, randomVal, complement});
              }
              Log.d("Device " + this.name + ": Device requested Bluetooth reset.");
              closeConnection();
            } else if (sysexCommand == IS_ALIVE) {
              respondToIsAlive();
            } else {
              onSysex(sysexCommand, sysexData);
            }
          }
        } else {
          onSysex(sysexCommand, new byte[] {});
        }

      } else {
        if (sysexBytesRead < storedInputData.length) {
          storedInputData[sysexBytesRead] = inputData;
          sysexBytesRead++;
        }
      }
    } else if (waitForData > 0 && (int) (inputData & 0xFF) < 128) {
      waitForData--;
      storedInputData[waitForData] = inputData;
      if (executeMultiByteCommand != 0 && waitForData == 0) {
        switch (executeMultiByteCommand) {
          case DIGITAL_MESSAGE:
            setDigitalInputs(multiByteChannel, (storedInputData[0] << 7) + storedInputData[1]);
            break;
          case REPORT_VERSION:
            setVersion(storedInputData[0], storedInputData[1]);
            isFirmwareVersionQueried = true;
            break;
        }
      }
    } else {
      if ((int) (inputData & 0xFF) < 0xF0) {
        command = (byte) (inputData & 0xF0);
        multiByteChannel = (byte) (inputData & 0x0F);
      } else {
        command = inputData;
      }
      switch (command) {
        case START_SYSEX:
          parsingSysex = true;
          sysexBytesRead = 0;
          break;
        case DIGITAL_MESSAGE:
        case REPORT_VERSION:
          waitForData = 2;
          executeMultiByteCommand = command;
          break;
      }
    }
  }
    @Override
    public void run() {
      while (!this.isInterrupted()) {
        try {
          while ((readByteFromSerialBuffer()) != ShieldFrame.START_OF_FRAME) ;
          if (ShieldFrameTimeout != null) ShieldFrameTimeout.stopTimer();
          ShieldFrameTimeout = new TimeOut(1000);
          int tempArduinoLibVersion = readByteFromSerialBuffer();
          byte shieldId = readByteFromSerialBuffer();
          byte instanceId = readByteFromSerialBuffer();
          byte functionId = readByteFromSerialBuffer();
          ShieldFrame frame = new ShieldFrame(shieldId, functionId);
          int argumentsNumber = readByteFromSerialBuffer() & 0xFF;
          int argumentsNumberVerification = (255 - (readByteFromSerialBuffer() & 0xFF));
          if (argumentsNumber != argumentsNumberVerification) {
            Log.d(
                "Device "
                    + OneSheeldDevice.this.name
                    + ": Frame is incorrect, canceling what we've read so far.");
            if (ShieldFrameTimeout != null) ShieldFrameTimeout.stopTimer();
            serialBuffer.clear();
            continue;
          }
          boolean continueRequested = false;
          for (int i = 0; i < argumentsNumber; i++) {
            int length = readByteFromSerialBuffer() & 0xFF;
            int lengthVerification = (255 - (readByteFromSerialBuffer() & 0xFF));
            if (length != lengthVerification || length <= 0) {
              Log.d(
                  "Device "
                      + OneSheeldDevice.this.name
                      + ": Frame is incorrect, canceling what we've read so far.");
              if (ShieldFrameTimeout != null) ShieldFrameTimeout.stopTimer();
              serialBuffer.clear();
              continueRequested = true;
              break;
            }
            byte[] data = new byte[length];
            for (int j = 0; j < length; j++) {
              data[j] = readByteFromSerialBuffer();
            }
            frame.addArgument(data);
          }
          if (continueRequested) continue;
          if ((readByteFromSerialBuffer()) != ShieldFrame.END_OF_FRAME) {
            Log.d(
                "Device "
                    + OneSheeldDevice.this.name
                    + ": Frame is incorrect, canceling what we've read so far.");
            if (ShieldFrameTimeout != null) ShieldFrameTimeout.stopTimer();
            serialBuffer.clear();
            continue;
          }
          if (ShieldFrameTimeout != null) ShieldFrameTimeout.stopTimer();
          if (arduinoLibraryVersion != tempArduinoLibVersion) {
            arduinoLibraryVersion = tempArduinoLibVersion;
            isLibraryVersionQueried = true;
            Log.d(
                "Device "
                    + OneSheeldDevice.this.name
                    + ": Device replied with library version: "
                    + arduinoLibraryVersion
                    + ".");
            onLibraryVersionQueryResponse(arduinoLibraryVersion);
          }

          if (shieldId == CONFIGURATION_SHIELD_ID) {
            if (functionId == LIBRARY_VERSION_RESPONSE) {
            } else if (functionId == IS_HARDWARE_CONNECTED_QUERY) {
              notifyHardwareOfConnection();
            } else if (functionId == IS_CALLBACK_ENTERED) {
              callbackEntered();
            } else if (functionId == IS_CALLBACK_EXITED) {
              callbackExited();
            }
          } else {
            Log.d(
                "Device " + OneSheeldDevice.this.name + ": Frame received, values: " + frame + ".");
            for (OneSheeldDataCallback oneSheeldDataCallback : dataCallbacks) {
              oneSheeldDataCallback.onShieldFrameReceive(frame);
              if (OneSheeldSdk.getKnownShields().contains(shieldId)
                  && OneSheeldSdk.getKnownShields()
                      .getKnownShield(shieldId)
                      .getKnownFunctions()
                      .contains(KnownFunction.getFunctionWithId(functionId)))
                oneSheeldDataCallback.onKnownShieldFrameReceive(
                    OneSheeldSdk.getKnownShields().getKnownShield(shieldId), frame);
            }
          }
        } catch (InterruptedException e) {
          return;
        } catch (ShieldFrameNotComplete e) {
          Log.d(
              "Device "
                  + OneSheeldDevice.this.name
                  + ": Frame wasn't completed in 1 second, canceling what we've read so far.");
          if (ShieldFrameTimeout != null) ShieldFrameTimeout.stopTimer();
          ShieldFrameTimeout = null;
          continue;
        }
      }
    }