/// <summary>
        /// Primary function used to vibrate motors - send a bit string command
        /// </summary>
        /// <returns>error code resulting from Vibrat Motor command</returns>
        private error_t Vibrate_Motor(acmd_mode_t cmd_mode, byte motor, byte rhythm, byte magnitude, byte rhythm_cycles)
        {
            byte[] command_byte = { 0x00, 0x00 };
            error_t return_error = error_t.EMAX;

            // Validate parameters
            if (motor < 0 || rhythm < 0 || rhythm > 8
                || magnitude < 0 || magnitude > 8
                || rhythm_cycles < 0 || rhythm_cycles > 7) {

                return_error = error_t.EARG;
            }
            else {
                byte mode = 0x0;
                //byte motor = 0x0; // this is the first valid motor
                //byte rhythm = 0x7; //rhy H =7
                //byte magnitude = 0x0; //mag A = 0
                //byte rhythm_cycles = 0x6;
                // This equates to hex command < 01 36 >

                try {
                    return_error = change_acmd_mode(cmd_mode);

                    if (return_error == error_t.ESUCCESS) {
                        mode = (byte)cmd_mode;

                        // Send mode first, it is the LSB of the first byte in firmware struct active_command_t
                        command_byte[0] = (byte)((mode << 4) | (motor & 0xf));
                        // Send rhythm first, it is the LSB of the firmware struct vibration_t
                        command_byte[1] = (byte)(((rhythm & 0x7) << 5)
                            | ((magnitude & 0x3) << 3)
                            | (rhythm_cycles & 0x7));

                        return_error = SerialPortWriteData(command_byte, MAX_RESPONSE_TIMEOUT);

                        // BROWN OUT HANDLING - Error from sending msg can return odd
                        // error code if belt is currently in ACM.LRN.  Reasonable risk that
                        // this will not occur when main controller is running on battery.
                        // error = 0xFC = 252 is from brown out condition
                        if (return_error > error_t.NOTFOUND) {

                            return_error = ResetHapticBelt();//error_t.ELOWPOWER;

                            // proceed for expected behavior of readtimeout
                            if (return_error == error_t.ESUCCESS)
                                //Try again with recursive call
                                return_error = Vibrate_Motor(cmd_mode, motor, rhythm, magnitude, rhythm_cycles);
                        }
                    }
                }
                catch (Exception e) {
                    return_error = error_t.EXCVIBCMD;
                }
            }
            _dll_error = return_error;
            return return_error;
        }
        /*
         * This method is used to change the mode of the application
         * as the firmware mode is changed.  The application and the
         * firmware modes must stay synchronized.
         */
        private error_t change_acmd_mode(acmd_mode_t mode)
        {
            error_t return_error = error_t.EMAX;

            if (_acmd_mode == mode) {
                _belt_error = error_t.ESUCCESS; // already in requested mode
                return_error = _belt_error;
            }
            else if (mode == acmd_mode_t.ACM_LRN) {
                //return to learning mode
                return_error = change_glbl_mode(mode_t.M_LEARN);
                if (return_error == error_t.ESUCCESS) {
                    _acmd_mode = mode;
                }
            }
            else { //(mode == acmd_mode_t.ACM_VIB || acmd_mode_t.ACM_SPT || acmd_mode_t.ACM_GCL)
                return_error = change_glbl_mode(mode_t.M_ACTIVE);
                if (return_error == error_t.ESUCCESS) {
                    _acmd_mode = mode;
                }
            }
            _dll_error = return_error;
            return return_error;
        }
        /*
         * Function is used to get status from belt after each send or receive
         */
        private void checkBeltStatus(acmd_mode_t acmd_mode)
        {
            error_t error = error_t.EMAX;

            // .NET SerialPort.ReadLine() results in \r\n
            // BUT YOU CAN ONLY SEE "\n" WHEN DIRECTLY LISTENING TO PORT
            //if (serialIn.MsgInBuffer.Equals("STS 0\r\n") || serialIn.MsgInBuffer.Equals("53 54 53 20 30 0D 0A "))

            if (acmd_mode == acmd_mode_t.ACM_LRN) {
                char[] delimiters = new char[] { '\r', '\n', ' ' };
                string[] split = ByteToAscii(serialIn.DataRecvBuffer()).Split(delimiters);
                for (int i = 0; i < split.Length; i++) {
                    if (String.Equals(split[i], "STS")) {
                        byte[] errors = IntegerStrToByte(split[i + 1]); // gets first error code
                        error = (error_t)(errors[0]);
                    }
                }
            }
            // Otherwise the error code is returned as a hex digit without '\n'
            else if (acmd_mode == acmd_mode_t.ACM_VIB) {
                error = (error_t)(serialIn.DataRecvBuffer()[0]);
            }
            else { ; }//do nothing
            _belt_error = error;
        }
        /// <summary>
        /// Closes all COM ports in use for the wireless haptic belt.
        /// </summary>
        public error_t ClosePorts()
        {
            error_t error = error_t.COMPRTOPEN;

            if (serialIn != null && serialOut != null) {
                error = serialIn.ClosePort();
                if (_portInName != _portOutName)
                    error = serialOut.ClosePort();
            }
            // Reset mode variables & state machine
            _glbl_mode = mode_t.M_LEARN;
            _acmd_mode = acmd_mode_t.ACM_LRN;

            _dll_error = error;
            return error;
        }