@Override
    public void setMode(Pin pin, PinMode mode) {
        // validate
        if (!pin.getSupportedPinModes().contains(mode)) {
            throw new InvalidPinModeException(pin, "Invalid pin mode [" + mode.getName()
                    + "]; pin [" + pin.getName() + "] does not support this mode.");
        }
        // validate
        if (!pin.getSupportedPinModes().contains(mode)) {
            throw new UnsupportedPinModeException(pin, mode);
        }
        try {
            // determine register and pin address
            int pinAddress = pin.getAddress();

            // determine update direction value based on mode
            if (mode == PinMode.DIGITAL_INPUT) {
                currentDirection |= pinAddress;
            } else if (mode == PinMode.DIGITAL_OUTPUT) {
                currentDirection &= ~pinAddress;
            }

            // next update direction value
            device.write(REGISTER_IODIR, (byte) currentDirection);

            // enable interrupts; interrupt on any change from previous state
            device.write(REGISTER_GPINTEN, (byte) currentDirection);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }

        // cache mode
        getPinCache(pin).setMode(mode);

        // if any pins are configured as input pins, then we need to start the interrupt monitoring
        // thread
        if (currentDirection > 0) {
            // if the monitor has not been started, then start it now
            if (monitor == null) {
                // start monitoring thread
                monitor = new GpioStateMonitor(device);
                monitor.start();
            }
        } else {
            // shutdown and destroy monitoring thread since there are no input pins configured
            if (monitor != null) {
                monitor.shutdown();
                monitor = null;
            }
        }
    }