/* ================================================================================= File: FFConst.cpp Non-Visual Driving System A force feedback joystick is controlled by this program, and serial data is sent in and out that interfaces with custom logic that wirelessly communicates to a RC car fitted with four Sharp IR rangefinders. Joystick motions are sent to the car to control acceleration and steering. Rangefinder data is sent from the car and is the basis of feedback some code portions, and code modifications by Lucas Walter 2002 Example code take from Ramon de Klein's Serial library for C++ http://www.codeproject.com/system/serial.asp?&forumid=1996&select=127076 Much windows and directX specific code taken from example code in the DirectX SDK that is Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved. ================================================================================= */ #define STRICT #include #include #include #include "serial.h" #include "serialWnd.h" #include #include #include #include #include #include "resource.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //----------------------------------------------------------------------------- // Function prototypes //----------------------------------------------------------------------------- INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ); BOOL CALLBACK EnumFFDevicesCallback( const DIDEVICEINSTANCE* pInst, VOID* pContext ); BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext ); BOOL CALLBACK EnumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext ); // non MS created functions, from serial library examples INT ShowError ( LONG lError, LPCTSTR lptszMessage ); // non MS created functions INT JoyToSer( INT joy ); VOID SerToRange( BYTE* rangeBuffer ); HRESULT RangesToForce( HWND hDlg ); INT CoordToForce( INT x ); BYTE ReverseEndian( BYTE original ); INT InitComPort( HWND hDlg ); #define MOUSE_RANGE // simulate rangefinder data with mouse // MS created, but altered to a degree HRESULT InitDirectInput( HWND hDlg ); VOID FreeDirectInput(); VOID OnPaint( HWND hDlg ); #ifdef MOUSE_RANGE HRESULT OnMouseMove( HWND hDlg, INT newMouseX, INT newMouseY, UINT keyFlags ); VOID OnButtonDown( HWND hDlg, INT x, INT y, UINT keyFlags ); VOID OnButtonUp( HWND hDlg, INT x, INT y, UINT keyFlags ); #endif HRESULT SetDeviceForcesXY(); HRESULT UpdateInputState( HWND hDlg ); //----------------------------------------------------------------- // Defines, constants, and global variables //----------------------------------------------------------------- #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } #define FEEDBACK_WINDOW_X 20 #define FEEDBACK_WINDOW_Y 60 #define FEEDBACK_WINDOW_WIDTH 256 // cause the 'two-wall' vibration //#define VIBRATE_ON // raising this will increase vibration force #define VIBRATE_FACTOR 5 // raising this will dampen overall force feedback #define FORCE_FACTOR 1.2 // these are used for masking address bits #define THROTTLE_MASK 96 // onto serial output #define DIRECTION_MASK 64 #define RANGE_LEFT_MASK 2 #define RANGE_RIGHT_MASK 1 // 80 cm is the maximum of the IR rangefinders #define RANGE_MAX 80 // must be multiple of 4 #ifdef VIBRATE_ON #define INDEX_MAX 24 #define RANGE_BUFFER_SIZE 50 // number of updates, serial output/input //cycles per second, >100 values don't really work #define FREQ 60 // the circuits are clocked 16x = 153.6 KHz #define BAUDRATE (CSerial::EBaudrate)9600 LPDIRECTINPUT8 g_pDI = NULL; LPDIRECTINPUTDEVICE8 g_pDevice = NULL; LPDIRECTINPUTEFFECT g_pEffect = NULL; BOOL g_bActive = TRUE; DWORD g_dwNumForceFeedbackAxis = 0; INT g_nXForce; INT g_nYForce; // Time of the previous force feedback effect set DWORD g_dwLastEffectSet; // rangefinder data, reflected by bounding box, // currently simulated with mouse INT boundRight = FEEDBACK_WINDOW_WIDTH; INT boundLeft = 0; INT boundTop = 0; INT boundBottom = FEEDBACK_WINDOW_WIDTH; // data from range finders, converted to cm BYTE rangeFront = 0; BYTE rangeBack = 0; BYTE rangeLeft = 0; BYTE rangeRight = 0; // Joystick information INT joyX; INT joyY; INT index = 0; INT oldMouseX = 0; INT oldMouseY = 0; INT throttle; INT direction; // button held down last mouse move BOOL lDown = FALSE; BOOL rDown = FALSE; CSerialWnd serial; /* ========================================================================== Lookup table Scale values from rangefinders to cm The rangefinders detect between 10 and 80 cm in a nonlinear fashion This table was generatedexperimentally. ========================================================================== */ BYTE rangeFrontTocm[256] = { 8, // 0 8, // 1 8, // 2 9, // 3 9, // 4 9, // 5 10, // 6 10, // 7 10, // 8 10, // 9 10, // 10 10, // 11 10, // 12 11, // 13 11, // 14 11, // 15 11, // 16 11, // 17 12, // 18 12, // 19 12, // 20 12, // 21 12, // 22 13, // 23 13, // 24 13, // 25 13, // 26 14, // 27 14, // 28 14, // 29 15, // 30 15, // 31 15, // 32 15, // 33 16, // 34 16, // 35 16, // 36 17, // 37 17, // 38 17, // 39 17, // 40 19, // 41 19, // 42 20, // 43 20, // 44 21, // 45 21, // 46 22, // 47 22, // 48 23, // 49 24, // 50 24, // 51 25, // 52 26, // 53 27, // 54 28, // 55 29, // 56 30, // 57 30, // 58 32, // 59 34, // 60 35, // 61 37, // 62 39, // 63 41, // 64 43, // 65 45, // 66 47, // 67 51, // 68 55, // 69 60, // 70 65, // 71 // I used a perl script to generate this RANGE_MAX, /* 72 */ RANGE_MAX, /* 73 */ RANGE_MAX, /* 74 */ RANGE_MAX, /* 75 */ RANGE_MAX, /* 76 */ RANGE_MAX, /* 77 */ RANGE_MAX, /* 78 */ RANGE_MAX, /* 79 */ RANGE_MAX, /* 80 */ RANGE_MAX, /* 81 */ RANGE_MAX, /* 82 */ RANGE_MAX, /* 83 */ RANGE_MAX, /* 84 */ RANGE_MAX, /* 85 */ RANGE_MAX, /* 86 */ RANGE_MAX, /* 87 */ RANGE_MAX, /* 88 */ RANGE_MAX, /* 89 */ RANGE_MAX, /* 90 */ RANGE_MAX, /* 91 */ RANGE_MAX, /* 92 */ RANGE_MAX, /* 93 */ RANGE_MAX, /* 94 */ RANGE_MAX, /* 95 */ RANGE_MAX, /* 96 */ RANGE_MAX, /* 97 */ RANGE_MAX, /* 98 */ RANGE_MAX, /* 99 */ RANGE_MAX, /* 100 */ RANGE_MAX, /* 101 */ RANGE_MAX, /* 102 */ RANGE_MAX, /* 103 */ RANGE_MAX, /* 104 */ RANGE_MAX, /* 105 */ RANGE_MAX, /* 106 */ RANGE_MAX, /* 107 */ RANGE_MAX, /* 108 */ RANGE_MAX, /* 109 */ RANGE_MAX, /* 110 */ RANGE_MAX, /* 111 */ RANGE_MAX, /* 112 */ RANGE_MAX, /* 113 */ RANGE_MAX, /* 114 */ RANGE_MAX, /* 115 */ RANGE_MAX, /* 116 */ RANGE_MAX, /* 117 */ RANGE_MAX, /* 118 */ RANGE_MAX, /* 119 */ RANGE_MAX, /* 120 */ RANGE_MAX, /* 121 */ RANGE_MAX, /* 122 */ RANGE_MAX, /* 123 */ RANGE_MAX, /* 124 */ RANGE_MAX, /* 125 */ RANGE_MAX, /* 126 */ RANGE_MAX, /* 127 */ RANGE_MAX, /* 128 */ RANGE_MAX, /* 129 */ RANGE_MAX, /* 130 */ RANGE_MAX, /* 131 */ RANGE_MAX, /* 132 */ RANGE_MAX, /* 133 */ RANGE_MAX, /* 134 */ RANGE_MAX, /* 135 */ RANGE_MAX, /* 136 */ RANGE_MAX, /* 137 */ RANGE_MAX, /* 138 */ RANGE_MAX, /* 139 */ RANGE_MAX, /* 140 */ RANGE_MAX, /* 141 */ RANGE_MAX, /* 142 */ RANGE_MAX, /* 143 */ RANGE_MAX, /* 144 */ RANGE_MAX, /* 145 */ RANGE_MAX, /* 146 */ RANGE_MAX, /* 147 */ RANGE_MAX, /* 148 */ RANGE_MAX, /* 149 */ RANGE_MAX, /* 150 */ RANGE_MAX, /* 151 */ RANGE_MAX, /* 152 */ RANGE_MAX, /* 153 */ RANGE_MAX, /* 154 */ RANGE_MAX, /* 155 */ RANGE_MAX, /* 156 */ RANGE_MAX, /* 157 */ RANGE_MAX, /* 158 */ RANGE_MAX, /* 159 */ RANGE_MAX, /* 160 */ RANGE_MAX, /* 161 */ RANGE_MAX, /* 162 */ RANGE_MAX, /* 163 */ RANGE_MAX, /* 164 */ RANGE_MAX, /* 165 */ RANGE_MAX, /* 166 */ RANGE_MAX, /* 167 */ RANGE_MAX, /* 168 */ RANGE_MAX, /* 169 */ RANGE_MAX, /* 170 */ RANGE_MAX, /* 171 */ RANGE_MAX, /* 172 */ RANGE_MAX, /* 173 */ RANGE_MAX, /* 174 */ RANGE_MAX, /* 175 */ RANGE_MAX, /* 176 */ RANGE_MAX, /* 177 */ RANGE_MAX, /* 178 */ RANGE_MAX, /* 179 */ RANGE_MAX, /* 180 */ RANGE_MAX, /* 181 */ RANGE_MAX, /* 182 */ RANGE_MAX, /* 183 */ RANGE_MAX, /* 184 */ RANGE_MAX, /* 185 */ RANGE_MAX, /* 186 */ RANGE_MAX, /* 187 */ RANGE_MAX, /* 188 */ RANGE_MAX, /* 189 */ RANGE_MAX, /* 190 */ RANGE_MAX, /* 191 */ RANGE_MAX, /* 192 */ RANGE_MAX, /* 193 */ RANGE_MAX, /* 194 */ RANGE_MAX, /* 195 */ RANGE_MAX, /* 196 */ RANGE_MAX, /* 197 */ RANGE_MAX, /* 198 */ RANGE_MAX, /* 199 */ RANGE_MAX, /* 200 */ RANGE_MAX, /* 201 */ RANGE_MAX, /* 202 */ RANGE_MAX, /* 203 */ RANGE_MAX, /* 204 */ RANGE_MAX, /* 205 */ RANGE_MAX, /* 206 */ RANGE_MAX, /* 207 */ RANGE_MAX, /* 208 */ RANGE_MAX, /* 209 */ RANGE_MAX, /* 210 */ RANGE_MAX, /* 211 */ RANGE_MAX, /* 212 */ RANGE_MAX, /* 213 */ RANGE_MAX, /* 214 */ RANGE_MAX, /* 215 */ RANGE_MAX, /* 216 */ RANGE_MAX, /* 217 */ RANGE_MAX, /* 218 */ RANGE_MAX, /* 219 */ RANGE_MAX, /* 220 */ RANGE_MAX, /* 221 */ RANGE_MAX, /* 222 */ RANGE_MAX, /* 223 */ RANGE_MAX, /* 224 */ RANGE_MAX, /* 225 */ RANGE_MAX, /* 226 */ RANGE_MAX, /* 227 */ RANGE_MAX, /* 228 */ RANGE_MAX, /* 229 */ RANGE_MAX, /* 230 */ RANGE_MAX, /* 231 */ RANGE_MAX, /* 232 */ RANGE_MAX, /* 233 */ RANGE_MAX, /* 234 */ RANGE_MAX, /* 235 */ RANGE_MAX, /* 236 */ RANGE_MAX, /* 237 */ RANGE_MAX, /* 238 */ RANGE_MAX, /* 239 */ RANGE_MAX, /* 240 */ RANGE_MAX, /* 241 */ RANGE_MAX, /* 242 */ RANGE_MAX, /* 243 */ RANGE_MAX, /* 244 */ RANGE_MAX, /* 245 */ RANGE_MAX, /* 246 */ RANGE_MAX, /* 247 */ RANGE_MAX, /* 248 */ RANGE_MAX, /* 249 */ RANGE_MAX, /* 250 */ RANGE_MAX, /* 251 */ RANGE_MAX, /* 252 */ RANGE_MAX, /* 253 */ RANGE_MAX, /* 254 */ RANGE_MAX /* 255 */ }; //----------------------------------------------------------------------------- // Name: WinMain() // Desc: Entry point for the application. Since we use a simple dialog for // user interaction we don't need to pump messages. //----------------------------------------------------------------------------- INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT ) { // Display the main dialog box. DialogBox( hInst, MAKEINTRESOURCE(IDD_FORCE_FEEDBACK), NULL, MainDlgProc ); return TRUE; } //----------------------------------------------------------------------------- // Name: MainDlgProc // Desc: Handles dialog messages //----------------------------------------------------------------------------- INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { DWORD dwBytesRead = 0; BYTE abBuffer[100]; BYTE preamble = 254; if (msg == CSerialWnd::mg_nDefaultComMsg) { // A serial message occurred const CSerialWnd::EEvent eEvent = CSerialWnd::EEvent(LOWORD(wParam)); const CSerialWnd::EError eError = CSerialWnd::EError(HIWORD(wParam)); switch (eEvent) { case CSerialWnd::EEventRecv: // Read data, until there is nothing left, or just read once // because it'll never be that much? //do //{ // Read data from the COM-port serial.Read(abBuffer,sizeof(abBuffer),&dwBytesRead); if (dwBytesRead > 0) { /*if (abBuffer[0] == "U")*/ SerToRange(abBuffer); } //} //while (dwBytesRead == sizeof(abBuffer)); break; default: break; } } switch( msg ) { case WM_INITDIALOG: InitComPort(hDlg); if( FAILED( InitDirectInput( hDlg ) ) ) { MessageBox( NULL, _T("Error Initializing DirectInput ") _T("The sample will now exit."), _T("FFConst"), MB_ICONERROR | MB_OK ); EndDialog( hDlg, 0 ); } // Set a timer to go off 30 times a second. At every timer message // the input device will be read //SetTimer( hDlg, 0, 1000 / 30, NULL ); SetTimer( hDlg, 0, 1000/FREQ, NULL ); // Init the time of the last force feedback effect g_dwLastEffectSet = timeGetTime(); break; #ifdef MOUSE_RANGE case WM_MOUSEMOVE: if( FAILED( OnMouseMove( hDlg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam ) ) ) { MessageBox( NULL, _T("Error setting effect parameters. ") _T("The sample will now exit."), _T("FFConst"), MB_ICONERROR | MB_OK ); EndDialog( hDlg, 0 ); } break; case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: OnButtonDown( hDlg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam ); break; case WM_LBUTTONUP: case WM_RBUTTONUP: OnButtonUp( hDlg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam ); break; #endif case WM_PAINT: OnPaint( hDlg ); break; case WM_ACTIVATE: if( WA_INACTIVE != wParam && g_pDevice ) { // Make sure the device is acquired, if we are gaining focus. g_pDevice->Acquire(); if( g_pEffect ) g_pEffect->Start( 1, 0 ); // Start the effect } break; case WM_TIMER: // Update the input device every timer message RangesToForce( hDlg ); InvalidateRect( hDlg, 0, TRUE ); UpdateWindow( hDlg );// This set ON_PAINT message if( FAILED( UpdateInputState( hDlg ) ) ) { KillTimer( hDlg, 0 ); MessageBox( NULL, TEXT("Error Reading Input State. ") \ TEXT("The sample will now exit."), TEXT("DirectInput Sample"), MB_ICONERROR | MB_OK ); EndDialog( hDlg, TRUE ); } throttle = throttle | THROTTLE_MASK; direction = direction | DIRECTION_MASK; //serial.Write("U"); serial.Write((char*)&preamble); serial.Write((char*)&throttle); serial.Write((char*)&direction); return TRUE; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDCANCEL: EndDialog( hDlg, 0 ); break; default: return FALSE; // Message not handled } break; case WM_DESTROY: // Cleanup everything serial.Close(); KillTimer( hDlg, 0 ); FreeDirectInput(); break; default: return FALSE; // Message not handled } return TRUE; // Message handled } int ShowError (LONG lError, LPCTSTR lptszMessage) { // Generate a message text TCHAR tszMessage[256]; wsprintf(tszMessage,_T("%s\n(error code %d)"), lptszMessage, lError); // Display message-box and return with an error-code ::MessageBox(0,tszMessage,_T("Drive Without Seeing Feel Without Touching"), MB_ICONSTOP|MB_OK); return 1; } //----------------------------------------------------------------------------- // Name: InitDirectInput() // Desc: Initialize the DirectInput variables. //----------------------------------------------------------------------------- HRESULT InitDirectInput( HWND hDlg ) { DIPROPDWORD dipdw; HRESULT hr; // Register with the DirectInput subsystem and get a pointer // to a IDirectInput interface we can use. if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) ) { return hr; } // Look for a force feedback device we can use if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumFFDevicesCallback, NULL, DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK ) ) ) { return hr; } if( NULL == g_pDevice ) { MessageBox( NULL, _T("Force feedback device not found. ") _T("The sample will now exit."), _T("FFConst"), MB_ICONERROR | MB_OK ); EndDialog( hDlg, 0 ); return S_OK; } // Set the data format to "simple joystick" - a predefined data format. A // data format specifies which controls on a device we are interested in, // and how they should be reported. // // This tells DirectInput that we will be passing a DIJOYSTATE2 structure to // IDirectInputDevice8::GetDeviceState(). Even though we won't actually do // it in this sample. But setting the data format is important so that the // DIJOFS_* values work properly. if( FAILED( hr = g_pDevice->SetDataFormat( &c_dfDIJoystick2 ) ) ) return hr; // Set the cooperative level to let DInput know how this device should // interact with the system and with other DInput applications. // Exclusive access is required in order to perform force feedback. if( FAILED( hr = g_pDevice->SetCooperativeLevel( hDlg, DISCL_EXCLUSIVE | DISCL_FOREGROUND ) ) ) { return hr; } // Since we will be playing force feedback effects, we should disable the // auto-centering spring. dipdw.diph.dwSize = sizeof(DIPROPDWORD); dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); dipdw.diph.dwObj = 0; dipdw.diph.dwHow = DIPH_DEVICE; dipdw.dwData = FALSE; if( FAILED( hr = g_pDevice->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ) ) ) return hr; // Enumerate and count the axes of the joystick if ( FAILED( hr = g_pDevice->EnumObjects( EnumAxesCallback, (VOID*)&g_dwNumForceFeedbackAxis, DIDFT_AXIS ) ) ) return hr; // Enumerate the joystick objects. The callback function enabled user // interface elements for objects that are found, and sets the min/max // values property for discovered axes. if( FAILED( hr = g_pDevice->EnumObjects( EnumObjectsCallback, (VOID*)hDlg, DIDFT_ALL ) ) ) return hr; // This simple sample only supports one or two axis joysticks if( g_dwNumForceFeedbackAxis > 2 ) g_dwNumForceFeedbackAxis = 2; // This application needs only one effect: Applying raw forces. DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y }; LONG rglDirection[2] = { 0, 0 }; DICONSTANTFORCE cf = { 0 }; DIEFFECT eff; ZeroMemory( &eff, sizeof(eff) ); eff.dwSize = sizeof(DIEFFECT); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwDuration = INFINITE; eff.dwSamplePeriod = 0; eff.dwGain = DI_FFNOMINALMAX; eff.dwTriggerButton = DIEB_NOTRIGGER; eff.dwTriggerRepeatInterval = 0; eff.cAxes = g_dwNumForceFeedbackAxis; eff.rgdwAxes = rgdwAxes; eff.rglDirection = rglDirection; eff.lpEnvelope = 0; eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); eff.lpvTypeSpecificParams = &cf; eff.dwStartDelay = 0; // Create the prepared effect if( FAILED( hr = g_pDevice->CreateEffect( GUID_ConstantForce, &eff, &g_pEffect, NULL ) ) ) { return hr; } if( NULL == g_pEffect ) return E_FAIL; return S_OK; } //----------------------------------------------------------------------------- // Name: EnumObjectsCallback() // Desc: Callback function for enumerating objects (axes, buttons, POVs) on a // joystick. This function enables user interface elements for objects // that are found to exist, and scales axes min/max values. //----------------------------------------------------------------------------- BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext ) { HWND hDlg = (HWND)pContext; static int nSliderCount = 0; // Number of returned slider controls static int nPOVCount = 0; // Number of returned POV controls // For axes that are returned, set the DIPROP_RANGE property for the // enumerated axis in order to scale min/max values. if( pdidoi->dwType & DIDFT_AXIS ) { DIPROPRANGE diprg; diprg.diph.dwSize = sizeof(DIPROPRANGE); diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); diprg.diph.dwHow = DIPH_BYID; diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis diprg.lMin = -FEEDBACK_WINDOW_WIDTH/2+1; diprg.lMax = +FEEDBACK_WINDOW_WIDTH/2; // Set the range for the axis if( FAILED( g_pDevice->SetProperty( DIPROP_RANGE, &diprg.diph ) ) ) return DIENUM_STOP; } // Set the UI to reflect what objects the joystick supports if (pdidoi->guidType == GUID_XAxis) { EnableWindow( GetDlgItem( hDlg, IDC_X_AXIS ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_X_AXIS_TEXT ), TRUE ); } if (pdidoi->guidType == GUID_YAxis) { EnableWindow( GetDlgItem( hDlg, IDC_Y_AXIS ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_Y_AXIS_TEXT ), TRUE ); } if (pdidoi->guidType == GUID_ZAxis) { EnableWindow( GetDlgItem( hDlg, IDC_Z_AXIS ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_Z_AXIS_TEXT ), TRUE ); } if (pdidoi->guidType == GUID_RxAxis) { EnableWindow( GetDlgItem( hDlg, IDC_X_ROT ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_X_ROT_TEXT ), TRUE ); } if (pdidoi->guidType == GUID_RyAxis) { EnableWindow( GetDlgItem( hDlg, IDC_Y_ROT ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_Y_ROT_TEXT ), TRUE ); } if (pdidoi->guidType == GUID_RzAxis) { EnableWindow( GetDlgItem( hDlg, IDC_Z_ROT ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_Z_ROT_TEXT ), TRUE ); } if (pdidoi->guidType == GUID_Slider) { switch( nSliderCount++ ) { case 0 : EnableWindow( GetDlgItem( hDlg, IDC_SLIDER0 ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_SLIDER0_TEXT ), TRUE ); break; case 1 : EnableWindow( GetDlgItem( hDlg, IDC_SLIDER1 ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_SLIDER1_TEXT ), TRUE ); break; } } if (pdidoi->guidType == GUID_POV) { switch( nPOVCount++ ) { case 0 : EnableWindow( GetDlgItem( hDlg, IDC_POV0 ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_POV0_TEXT ), TRUE ); break; case 1 : EnableWindow( GetDlgItem( hDlg, IDC_POV1 ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_POV1_TEXT ), TRUE ); break; case 2 : EnableWindow( GetDlgItem( hDlg, IDC_POV2 ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_POV2_TEXT ), TRUE ); case 3 : EnableWindow( GetDlgItem( hDlg, IDC_POV3 ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_POV3_TEXT ), TRUE ); } } EnableWindow( GetDlgItem( hDlg, IDC_X_FORCE ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_X_FORCE_TEXT ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_Y_FORCE ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_Y_FORCE_TEXT ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_RANGE_FRONT ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_RANGE_BACK ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_RANGE_LEFT ), TRUE ); EnableWindow( GetDlgItem( hDlg, IDC_RANGE_RIGHT ), TRUE ); return DIENUM_CONTINUE; } //----------------------------------------------------------------------------- // Name: EnumAxesCallback() // Desc: Callback function for enumerating the axes on a joystick and counting // each force feedback enabled axis //----------------------------------------------------------------------------- BOOL CALLBACK EnumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext ) { DWORD* pdwNumForceFeedbackAxis = (DWORD*) pContext; if( (pdidoi->dwFlags & DIDOI_FFACTUATOR) != 0 ) (*pdwNumForceFeedbackAxis)++; return DIENUM_CONTINUE; } //----------------------------------------------------------------------------- // Name: EnumFFDevicesCallback() // Desc: Called once for each enumerated force feedback device. If we find // one, create a device interface on it so we can play with it. //----------------------------------------------------------------------------- BOOL CALLBACK EnumFFDevicesCallback( const DIDEVICEINSTANCE* pInst, VOID* pContext ) { LPDIRECTINPUTDEVICE8 pDevice; HRESULT hr; // Obtain an interface to the enumerated force feedback device. hr = g_pDI->CreateDevice( pInst->guidInstance, &pDevice, NULL ); // If it failed, then we can't use this device for some // bizarre reason. (Maybe the user unplugged it while we // were in the middle of enumerating it.) So continue enumerating if( FAILED(hr) ) return DIENUM_CONTINUE; // We successfully created an IDirectInputDevice8. So stop looking // for another one. g_pDevice = pDevice; return DIENUM_STOP; } //----------------------------------------------------------------------------- // Name: FreeDirectInput() // Desc: Initialize the DirectInput variables. //----------------------------------------------------------------------------- VOID FreeDirectInput() { // Unacquire the device one last time just in case // the app tried to exit while the device is still acquired. if( g_pDevice ) g_pDevice->Unacquire(); // Release any DirectInput objects. SAFE_RELEASE( g_pEffect ); SAFE_RELEASE( g_pDevice ); SAFE_RELEASE( g_pDI ); } //----------------------------------------------------------------------------- // Name: UpdateInputState() // Desc: Get the input device's state and display it. //----------------------------------------------------------------------------- HRESULT UpdateInputState( HWND hDlg ) { HRESULT hr; TCHAR strText[128]; // Device state text DIJOYSTATE2 js; // DInput joystick state TCHAR* str; if( NULL == g_pDevice ) return S_OK; // Poll the device to read the current state hr = g_pDevice->Poll(); if( FAILED(hr) ) { // DInput is telling us that the input stream has been // interrupted. We aren't tracking any state between polls, so // we don't have any special reset that needs to be done. We // just re-acquire and try again. hr = g_pDevice->Acquire(); while( hr == DIERR_INPUTLOST ) hr = g_pDevice->Acquire(); // hr may be DIERR_OTHERAPPHASPRIO or other errors. This // may occur when the app is minimized or in the process of // switching, so just try again later return S_OK; } // Get the input's device state if( FAILED( hr = g_pDevice->GetDeviceState( sizeof(DIJOYSTATE2), &js ) ) ) return hr; // The device should have been acquired during the Poll() // Set globals joyX = js.lX; joyY = js.lY; throttle = JoyToSer(js.rglSlider[0]); direction = JoyToSer(-js.lRz); // Display joystick state to dialog // Axes wsprintf( strText, TEXT("%ld"), js.lX ); SetWindowText( GetDlgItem( hDlg, IDC_X_AXIS ), strText ); wsprintf( strText, TEXT("%ld"), js.lY ); SetWindowText( GetDlgItem( hDlg, IDC_Y_AXIS ), strText ); wsprintf( strText, TEXT("%ld"), js.lZ ); SetWindowText( GetDlgItem( hDlg, IDC_Z_AXIS ), strText ); wsprintf( strText, TEXT("%ld"), js.lRx ); SetWindowText( GetDlgItem( hDlg, IDC_X_ROT ), strText ); wsprintf( strText, TEXT("%ld"), js.lRy ); SetWindowText( GetDlgItem( hDlg, IDC_Y_ROT ), strText ); wsprintf( strText, TEXT("%ld"), direction ); SetWindowText( GetDlgItem( hDlg, IDC_Z_ROT ), strText ); // Slider controls wsprintf( strText, TEXT("%ld"), throttle ); SetWindowText( GetDlgItem( hDlg, IDC_SLIDER0 ), strText ); wsprintf( strText, TEXT("%ld"), js.rglSlider[1] ); SetWindowText( GetDlgItem( hDlg, IDC_SLIDER1 ), strText ); // Points of view wsprintf( strText, TEXT("%ld"), js.rgdwPOV[0] ); SetWindowText( GetDlgItem( hDlg, IDC_POV0 ), strText ); wsprintf( strText, TEXT("%ld"), js.rgdwPOV[1] ); SetWindowText( GetDlgItem( hDlg, IDC_POV1 ), strText ); wsprintf( strText, TEXT("%ld"), js.rgdwPOV[2] ); SetWindowText( GetDlgItem( hDlg, IDC_POV2 ), strText ); wsprintf( strText, TEXT("%ld"), js.rgdwPOV[3] ); SetWindowText( GetDlgItem( hDlg, IDC_POV3 ), strText ); // Force summary wsprintf( strText, TEXT("%ld"), g_nXForce ); SetWindowText( GetDlgItem( hDlg, IDC_X_FORCE ) , strText ); wsprintf( strText, TEXT("%ld"), g_nYForce ); SetWindowText( GetDlgItem( hDlg, IDC_Y_FORCE ), strText ); // Range summary wsprintf( strText, TEXT("%d"), rangeFront ); SetWindowText( GetDlgItem( hDlg, IDC_RANGE_FRONT ), strText ); wsprintf( strText, TEXT("%d"), rangeBack ); SetWindowText( GetDlgItem( hDlg, IDC_RANGE_BACK ), strText ); wsprintf( strText, TEXT("%d"), rangeLeft ); SetWindowText( GetDlgItem( hDlg, IDC_RANGE_LEFT ), strText ); wsprintf( strText, TEXT("%d"), rangeRight ); SetWindowText( GetDlgItem( hDlg, IDC_RANGE_RIGHT ), strText ); // Fill up text with which buttons are pressed str = strText; for( int i = 0; i < 128; i++ ) { if ( js.rgbButtons[i] & 0x80 ) str += wsprintf( str, TEXT("%02d "), i ); } *str = 0; // Terminate the string SetWindowText( GetDlgItem( hDlg, IDC_BUTTONS ), strText ); return S_OK; } //----------------------------------------------------------------------------- // Name: OnPaint() // Draws the FF boundary box //----------------------------------------------------------------------------- VOID OnPaint( HWND hDlg ) { PAINTSTRUCT ps; HDC hDC; HPEN hpenOld; HPEN hpenBlack; HBRUSH hbrOld; HBRUSH hbrBlack; INT x; INT y; hDC = BeginPaint( hDlg, &ps ); if( NULL == hDC ) return; // Everything is scaled to the size of the window. hpenBlack = GetStockPen( BLACK_PEN ); hpenOld = SelectPen( hDC, hpenBlack ); // Draw force feedback bounding rect // but don't show lines where there's no rangefinder // range returned MoveToEx( hDC, FEEDBACK_WINDOW_X + boundLeft, FEEDBACK_WINDOW_Y + boundBottom, NULL ); if (boundLeft != 0) { LineTo( hDC, FEEDBACK_WINDOW_X + boundLeft, FEEDBACK_WINDOW_Y + boundTop ); } else MoveToEx( hDC, FEEDBACK_WINDOW_X + boundLeft, FEEDBACK_WINDOW_Y + boundTop, NULL ); if (boundTop != 0) { LineTo( hDC, FEEDBACK_WINDOW_X + boundRight, FEEDBACK_WINDOW_Y + boundTop ); } else MoveToEx( hDC, FEEDBACK_WINDOW_X + boundRight, FEEDBACK_WINDOW_Y + boundTop, NULL ); if (boundRight != FEEDBACK_WINDOW_WIDTH) { LineTo( hDC, FEEDBACK_WINDOW_X + boundRight, FEEDBACK_WINDOW_Y + boundBottom); } else MoveToEx( hDC, FEEDBACK_WINDOW_X + boundRight, FEEDBACK_WINDOW_Y + boundBottom, NULL ); if (boundBottom != FEEDBACK_WINDOW_WIDTH) { LineTo( hDC, FEEDBACK_WINDOW_X + boundLeft, FEEDBACK_WINDOW_Y + boundBottom); } // Calculate center of feedback window for center marker x = FEEDBACK_WINDOW_X + FEEDBACK_WINDOW_WIDTH / 2; y = FEEDBACK_WINDOW_Y + FEEDBACK_WINDOW_WIDTH / 2; // Draw joystick x,y position marker MoveToEx( hDC, x + joyX - 5, y + joyY, NULL); LineTo( hDC, x + joyX + 5, y + joyY); MoveToEx( hDC, x + joyX, y + joyY - 5, NULL); LineTo( hDC, x + joyX, y + joyY + 5); // Draw center marker MoveToEx( hDC, x, y - 10, NULL ); LineTo( hDC, x, y + 10 + 1 ); MoveToEx( hDC, x - 10, y, NULL ); LineTo( hDC, x + 10 + 1, y ); hbrBlack = GetStockBrush( BLACK_BRUSH ); hbrOld = SelectBrush( hDC, hbrBlack ); x = MulDiv( FEEDBACK_WINDOW_WIDTH, g_nXForce + DI_FFNOMINALMAX, 2 * DI_FFNOMINALMAX ); y = MulDiv( FEEDBACK_WINDOW_WIDTH, g_nYForce + DI_FFNOMINALMAX, 2 * DI_FFNOMINALMAX ); x += FEEDBACK_WINDOW_X; y += FEEDBACK_WINDOW_Y; Ellipse( hDC, x-5, y-5, x+6, y+6 ); SelectBrush( hDC, hbrOld ); SelectPen( hDC, hpenOld ); EndPaint( hDlg, &ps ); } #ifdef MOUSE_RANGE //----------------------------------------------------------------------------- // Name: OnMouseMove() // Desc: For the purposes of testing the FF joystick and simulating // values that are normally taken from serial input // // If a mouse button is down, then change the direction of // the force to match the new location. // The right buttom moves the right and top borders // The left button moves the left and bottom borders //----------------------------------------------------------------------------- HRESULT OnMouseMove( HWND hDlg, INT newMouseX, INT newMouseY, UINT keyFlags ) { INT dX = newMouseX - oldMouseX; INT dY = newMouseY - oldMouseY; if( NULL == g_pEffect ) return S_OK; if(( keyFlags & MK_LBUTTON ) && lDown) { boundLeft += dX; boundBottom += dY; } if(( keyFlags & MK_RBUTTON ) && rDown) { boundRight += dX; boundTop += dY; } // keep boundaries within...bounds if (boundRight > FEEDBACK_WINDOW_WIDTH) boundRight = FEEDBACK_WINDOW_WIDTH; if (boundRight < FEEDBACK_WINDOW_WIDTH/2) boundRight = FEEDBACK_WINDOW_WIDTH/2; if (boundTop > FEEDBACK_WINDOW_WIDTH) boundTop = FEEDBACK_WINDOW_WIDTH; if (boundTop < FEEDBACK_WINDOW_WIDTH/2) boundTop = FEEDBACK_WINDOW_WIDTH/2; if (boundLeft < 0) boundLeft = 0; if (boundLeft > FEEDBACK_WINDOW_WIDTH/2) boundLeft = FEEDBACK_WINDOW_WIDTH/2; if (boundBottom < 0) boundBottom = 0; if (boundBottom > FEEDBACK_WINDOW_WIDTH/2) boundBottom = FEEDBACK_WINDOW_WIDTH/2; if ( keyFlags & MK_LBUTTON ) lDown = TRUE; else lDown = FALSE; if ( keyFlags & MK_RBUTTON ) rDown = TRUE; else rDown = FALSE; oldMouseX = newMouseX; oldMouseY = newMouseY; return S_OK; } //----------------------------------------------------------------------------- // Name: OnButtonDown() // Desc: Capture the mouse so we can follow it, and start updating the // force information. //----------------------------------------------------------------------------- VOID OnButtonDown( HWND hDlg, INT x, INT y, UINT keyFlags ) { SetCapture( hDlg ); if (keyFlags & MK_RBUTTON | MK_LBUTTON ) OnMouseMove( hDlg, x, y, keyFlags ); } //----------------------------------------------------------------------------- // Name: OnLeftButtonUp() // Desc: Stop capturing the mouse when the button goes up. //----------------------------------------------------------------------------- VOID OnButtonUp( HWND hDlg, INT x, INT y, UINT keyFlags ) { ReleaseCapture(); } #endif // #ifdef MOUSE_RANGE //----------------------------------------------------------------------------- // Name: SetDeviceForcesXY() // Desc: Apply the X and Y forces to the effect we prepared. //----------------------------------------------------------------------------- HRESULT SetDeviceForcesXY() { // Modifying an effect is basically the same as creating a new one, except // you need only specify the parameters you are modifying LONG rglDirection[2] = { 0, 0 }; DICONSTANTFORCE cf; if( g_dwNumForceFeedbackAxis == 1 ) { // If only one force feedback axis, then apply only one direction and // keep the direction at zero cf.lMagnitude = g_nXForce; rglDirection[0] = 0; } else { // If two force feedback axis, then apply magnitude from both directions rglDirection[0] = g_nXForce; rglDirection[1] = g_nYForce; cf.lMagnitude = (DWORD)sqrt( (double)g_nXForce * (double)g_nXForce + (double)g_nYForce * (double)g_nYForce ); } DIEFFECT eff; ZeroMemory( &eff, sizeof(eff) ); eff.dwSize = sizeof(DIEFFECT); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.cAxes = g_dwNumForceFeedbackAxis; eff.rglDirection = rglDirection; eff.lpEnvelope = 0; eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); eff.lpvTypeSpecificParams = &cf; eff.dwStartDelay = 0; // Now set the new parameters and start the effect immediately. return g_pEffect->SetParameters( &eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START ); } /* ============================================================================= JoyToSer Format Joystick data into 'signed' (the MSB is functions as a sign) serial data ranging from 0 to 31 ============================================================================= */ INT JoyToSer( int joy ) { int ser = joy >> 3; // the PWM motor control takes an int 0 - 15 // the difference (16-ser)/16 is the pulse width // thus a 0 or 16 is the maximum, and 15 and 31 minimum speed if (ser < 0) ser = 16 + ser; else if (ser == 0) ser = 31; else ser = 32 + -ser; if ((ser == 16) || (ser == 17)) ser = 18; if ((ser == 0) || (ser == 1)) ser = 2; return ser; } /* ============================================================================= read buffered serial input as ranges from two GP2D02 and two GP2D15 rangefinders ============================================================================= */ VOID SerToRange(BYTE* rangeBuffer) { // rangeBuffer[0] is an ASCII "U" for synching the transceivers // GP2D02 rangefinders send data MSB first // while PC reads first serial bit as LSB // Also, closer ranges correspond to higher returned // bytes from the rangefinders, so we must reverse them rangeFront = rangeFrontTocm[255 - ReverseEndian(rangeBuffer[0])]; rangeBack = rangeFrontTocm[255 - ReverseEndian(rangeBuffer[1])]; // these are the GP2D15's, which only have binary output // high - object in range // low - nothing in range // the two bits are in the 'MASKs and the rest of the // byte is arbitrarily 1's rangeLeft = ((rangeBuffer[2] & RANGE_LEFT_MASK ) > 0); rangeRight = ((rangeBuffer[2] & RANGE_RIGHT_MASK) > 0); boundBottom = FEEDBACK_WINDOW_WIDTH/2 + (FEEDBACK_WINDOW_WIDTH/2 * (rangeBack) )/RANGE_MAX; boundRight = FEEDBACK_WINDOW_WIDTH/4 * (4 - rangeRight); boundLeft = FEEDBACK_WINDOW_WIDTH/4 * (rangeLeft); boundTop = (FEEDBACK_WINDOW_WIDTH/2 * (RANGE_MAX - rangeFront) )/RANGE_MAX; } /* ================================================================================ Convert bounding box borders to force feedback ================================================================================ */ HRESULT RangesToForce(HWND hDlg) { HRESULT hr; INT max = FEEDBACK_WINDOW_WIDTH; // magnifies feedback when the joystick is pressed towards // the direction where there is a barrier present // problems arise when there are two opposite barriers, // and a loose hold on the joystick // will have it bouncing back and forth between them. if ((boundLeft == 0) && (boundRight == FEEDBACK_WINDOW_WIDTH)) { // no objects rightward or leftward g_nXForce = 0; } else if (boundRight == FEEDBACK_WINDOW_WIDTH) { INT tempJoyX = -joyX + max/2; g_nXForce = -DI_FFNOMINALMAX * boundLeft * tempJoyX/(max*max/2); } else if (boundLeft == 0) { INT tempBoundR = -boundRight + max; INT tempJoyX = joyX + max/2; g_nXForce = DI_FFNOMINALMAX * tempBoundR * tempJoyX/(max*max/2); } else { // two objects on either side INT tempJoyX = -joyX + max/2; // avgBound <- [0,max/2] INT avgBound = (boundRight + boundLeft)/2 - max/4; g_nXForce = -(DI_FFNOMINALMAX * (2 * (tempJoyX + avgBound) - (max+max/2)))/(max+max/2); } if ((boundBottom == FEEDBACK_WINDOW_WIDTH) && (boundTop == 0)) { // no objects forward or backward g_nYForce = 0; } else if (boundTop == FEEDBACK_WINDOW_WIDTH) { INT tempJoyY = -joyY + max/2; g_nYForce = -DI_FFNOMINALMAX * boundBottom * tempJoyY/(max*max/2); } else if (boundBottom == 0) { INT tempBoundT = -boundTop + max; INT tempJoyY = joyY + max/2; g_nYForce = DI_FFNOMINALMAX * tempBoundT * tempJoyY/(max*max/2); } else { // two objects on either side INT tempJoyY = -joyY + max/2; // avgBound <- [0,max/2] INT avgBound = (boundTop + boundBottom)/2 - max/4; g_nYForce = -(DI_FFNOMINALMAX * (2 * (tempJoyY + avgBound) - (max+max/2)))/(max+max/2); } #ifdef VIBRATE_ON index++; // vibration for two walls, annoying in practice if ((index % 4 == 0) && (boundRight != FEEDBACK_WINDOW_WIDTH)) { g_nXForce += -(MulDiv( boundLeft + FEEDBACK_WINDOW_WIDTH/2, 2 * DI_FFNOMINALMAX, FEEDBACK_WINDOW_WIDTH ) - DI_FFNOMINALMAX)/VIBRATE_FACTOR; } else if ((index % 3 == 0) && (boundBottom != 0)) { g_nYForce += -(MulDiv( boundTop - FEEDBACK_WINDOW_WIDTH/2, 2 * DI_FFNOMINALMAX, FEEDBACK_WINDOW_WIDTH ) - DI_FFNOMINALMAX)/VIBRATE_FACTOR; } else if ((index % 2 == 0) && (boundLeft != 0)) { g_nXForce += -(MulDiv( boundRight - FEEDBACK_WINDOW_WIDTH/2, 2 * DI_FFNOMINALMAX, FEEDBACK_WINDOW_WIDTH ) - DI_FFNOMINALMAX)/VIBRATE_FACTOR; } else if (boundTop != FEEDBACK_WINDOW_WIDTH) { g_nYForce += -(MulDiv( boundBottom + FEEDBACK_WINDOW_WIDTH/2, 2 * DI_FFNOMINALMAX, FEEDBACK_WINDOW_WIDTH ) - DI_FFNOMINALMAX)/VIBRATE_FACTOR; } if (index >= INDEX_MAX) index = 0; #endif // VIBRATE_ON // Keep force within bounds if( g_nXForce < -(int)(DI_FFNOMINALMAX/FORCE_FACTOR) ) g_nXForce = -(int)(DI_FFNOMINALMAX/FORCE_FACTOR); if( g_nXForce > +(int)(DI_FFNOMINALMAX/FORCE_FACTOR) ) g_nXForce = +(int)(DI_FFNOMINALMAX/FORCE_FACTOR); if( g_nYForce < -(int)(DI_FFNOMINALMAX/FORCE_FACTOR) ) g_nYForce = -(int)(DI_FFNOMINALMAX/FORCE_FACTOR); if( g_nYForce > +(int)(DI_FFNOMINALMAX/FORCE_FACTOR) ) g_nYForce = +(int)(DI_FFNOMINALMAX/FORCE_FACTOR); if( FAILED( hr = SetDeviceForcesXY() ) ) return hr; return S_OK; } /* ============================================================================= Reorder the bits of a byte to become opposite endian ============================================================================= */ BYTE ReverseEndian(BYTE original) { return ( ((original & 128) > 0) * 1 + ((original & 64) > 0) * 2 + ((original & 32) > 0) * 4 + ((original & 16) > 0) * 8 + ((original & 8) > 0) * 16 + ((original & 4) > 0) * 32 + ((original & 2) > 0) * 64 + ((original & 1) > 0) * 128 ); } /* ============================================================================= Set bps, parity, bitlength, etc, for comport, and open it ============================================================================= */ INT InitComPort( HWND hDlg ) { LONG lLastError = ERROR_SUCCESS; // Attempt to open the serial port (COM1) lLastError = serial.Open(_T("COM1"), hDlg, WM_NULL, 0, 2048, 2048 ); if (lLastError != ERROR_SUCCESS) return ::ShowError(serial.GetLastError(), _T("Unable to open COM-port")); // Setup the serial port (2400,N81) using hardware handshaking // // for some reason the 10KHz baudrate comes out a little fast - bits are 95 us wide instead of 100 // so far this hasn't resulted in too many misread bits // //lLastError = serial.Setup(CSerial::EBaud2400,CSerial::EData8,CSerial::EParNone,CSerial::EStop1); lLastError = serial.Setup(BAUDRATE,CSerial::EData8,CSerial::EParNone,CSerial::EStop1); if (lLastError != ERROR_SUCCESS) return ::ShowError(serial.GetLastError(), _T("Unable to set COM-port setting")); // Setup handshaking to off lLastError = serial.SetupHandshaking(CSerial::EHandshakeOff); if (lLastError != ERROR_SUCCESS) return ::ShowError(serial.GetLastError(), _T("Unable to set COM-port handshaking off")); serial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking); /* lLastError = serial.Write("U"); if (lLastError != ERROR_SUCCESS) return ::ShowError(serial.GetLastError(), _T("Unable to send data"));*/ return 0; } /* for (i = 0; i < RANGE_BUFFER_SIZE; i++) { rangeFrontBuffer[i] = 0; rangeBackBuffer[i] = 0; rangeLeftBuffer[i] = 0; rangeRightBuffer[i] = 0; }*/ // rangebuffering code /* // abBuffer[0] is an ASCII "U" for synching the transceivers rangeFrontBuffer[RANGE_BUFFER_SIZE-1] = rangeBuffer[1]; //RANGE_MAX - rangeLeftBuffer[RANGE_BUFFER_SIZE-1] = ((rangeBuffer[3] & RANGE_LEFT_MASK ) > 0); rangeRightBuffer[RANGE_BUFFER_SIZE-1] = ((rangeBuffer[3] & RANGE_RIGHT_MASK) > 0); rangeBackBuffer[RANGE_BUFFER_SIZE-1] = rangeBuffer[2]; // RANGE_MAX - INT sumRangeFront = 0; INT sumRangeLeft = 0; INT sumRangeRight = 0; INT sumRangeBack = 0; for (int i = 0; i < RANGE_BUFFER_SIZE - 1; i++) { rangeFrontBuffer[i] = rangeFrontBuffer[i+1]; rangeLeftBuffer[i] = rangeLeftBuffer[i+1]; rangeRightBuffer[i] = rangeRightBuffer[i+1]; rangeBackBuffer[i] = rangeBackBuffer[i+1]; sumRangeFront += rangeFrontBuffer[i+1]; sumRangeLeft += rangeLeftBuffer[i+1]; sumRangeRight += rangeRightBuffer[i+1]; sumRangeBack += rangeBackBuffer[i+1]; } sumRangeFront += rangeFrontBuffer[0]; sumRangeLeft += rangeLeftBuffer[0]; sumRangeRight += rangeRightBuffer[0]; sumRangeBack += rangeBackBuffer[0]; rangeFront = rangeFrontBuffer[RANGE_BUFFER_SIZE-1]; //(sumRangeFront/RANGE_BUFFER_SIZE); rangeLeft = (sumRangeLeft/RANGE_BUFFER_SIZE); rangeRight = (sumRangeRight/RANGE_BUFFER_SIZE); rangeBack = rangeBackBuffer[RANGE_BUFFER_SIZE-1]; //(sumRangeBack/RANGE_BUFFER_SIZE); */