diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/DJI_RC-N1_SIMULATOR_FLY_DCL.iml b/.idea/DJI_RC-N1_SIMULATOR_FLY_DCL.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/DJI_RC-N1_SIMULATOR_FLY_DCL.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d56657a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..2ea9f7b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DJI-RC-N1-Remote-Controller.png b/DJI-RC-N1-Remote-Controller.png new file mode 100644 index 0000000..b6e20cb Binary files /dev/null and b/DJI-RC-N1-Remote-Controller.png differ diff --git a/README.md b/README.md index 5b07840..9f61a51 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,66 @@ -mDjiController +DJI RC-N1 =============== - - Connect your DJI Remote Controller to your PC and use it to play simulators. - - Currently confirmed working controllers: DJI Phantom 2 +Discussion thread: coming soon... + +Donate: + + + + + +- https://send.monobank.ua/jar/87uNmHPh5v +- https://www.buymeacoffee.com/ivanyakymenko + +----------------------------------------------------------------------------- +Latest version V3.0.0 (26.09.2023) + - critical fix, com port open error on some PC's +----------------------------------------------------------------------------- + + - Connect your DJI Remote Controller to your PC and use it to play simulators + - Confirmed DJI Mavic 3 RC231 - For Mavic Mini or a Python interface, take a look at [justin97530/miniDjiController](https://github.com/justin97530/miniDjiController) - For DJI Mini 2 or a Python interface, take a look at [usatenko/DjiMini2RCasJoystick](https://github.com/usatenko/DjiMini2RCasJoystick) - For DJI Phantom 3 take a look at [mishavoloshchuk/mDjiController](https://github.com/mishavoloshchuk/mDjiController) ----------------------------------------------------------------------------- -This is a program that connects to your DJI Phantom Remote Controller (RC), -reads the stick positions and tells Windows that position. A simplified, wannabe driver. +This is a program that connects to your DJI Mavic 3 Remote Controller (RC-N1) as a XBox360 gamepad, +reads the stick positions and tells Windows that position. -In use: -![](https://raw.githubusercontent.com/Matsemann/mDjiController/master/in_use.png) - -Here I'm flying a DJI Phantom in HELI-X5 using mDjiController. Open up the image in big version to see the controller configuration. + +----------------------------------------------------------------------------- Installation / Usage ------------- - -### For a thorough guide on how to get started, see this blog post: [Using a Phantom 2 DJI Controller in AirSim](https://timebutt.github.io/static/using-a-phantom-dji-controller-in-airsim/) - -### UPDATE: Slaterbbx has added more features to this, you should also check out his fork of this here: https://github.com/slaterbbx/mDjiController +- Install packages before usage: +- dji-assistant-2-consumer-drones-series and close it after installation https://www.dji.com/downloads/softwares/dji-assistant-2-consumer-drones-series +- python 3.9 or 3.x +- pip3 install vgamepad +- pip3 install pyserial -In order for it to work, the original drivers from DJI must be installed, and something called vJoy. +- Power on RC-N1 +- Connect via bottom type-C +- run main.py +- run yor simulator -* Download mDjiController.zip from above ( https://github.com/Matsemann/mDjiController/blob/master/mDjiController.zip?raw=true ) -* Download DJI WIN Driver Installer from here: http://www.dji.com/product/phantom-2/download -* Download vJoy from here: http://vjoystick.sourceforge.net/site/index.php/download-a-install/72-download +![](connect_ok.png) -* Install both. -* Then connect your RC to your computer via USB and turn it on. +for restart game or recover drone: use camera wheel, left scroll -* Run mDjiController.exe, and select the correct COM port. By default you should try writing "3". -* For vJoy, write 1 unless you use vJoy already and have different configurations. +![](control.png) -Then open a simulator and calibrate the controller. -How does it work? ------------------ +TROUBLESHOOTING +----------------------------------------------------------------------------- +App searching by itself for the serial port with description "DJI USB VCOM For Protocol" +make sure your device attached via bottom Type-C connector +![](connect.png) -* It is possible to ask the controller for its status when it's connected. So this program continuously - reads the status. It connects through COM. The output from the controller is just a list of numbers, - but sampling many enough such lists it's easy to see a pattern for which number means what. The biggest issue - was that the numbers are little endian encoded and uses two's complement, so it took some time to understand how - each number behaved. - -* vJoy is a virtual joystick that can be installed on your computer. Windows think it's a normal joystick. mDjiController - takes the stick positions from the controller and tells Windows that this virtual joystick has the same positions. +[Tested with DCL - The game](https://store.steampowered.com/app/964570/DCL__The_Game/) + Preset: + Mode 2 + Acro + Zero throttle at stick center -TROUBLESHOOTING ---------------- -* If something doesn't work, make sure you have enabled logging, it may tell you the error. - -* COM can't connect - Try with other numbers instead of 3. Open up device-manager. - You should see a category named "Ports (COM & LPT) and then a device named "DJI USB Virtual COM (COMX)" - The X should be the number you should use. - If there is nothing there, the driver from DJI is not installed correctly. - - If you have any of the DJI Assistant Software installed, make sure they are not running, as this program - cannot connect then. - -* vJoy can't connect - The error message should tell you if it's installed or not. Install the vJoy driver if it's not there. - -* Not all sticks or buttons work - *If they look correct in the log:* - Your vJoy configuration may be wrong. Open "Configure vJoy" and reset configuration 1, or make a new one - and tell mDjiController.exe to use that configuration. - *If they are NOT correct in the log:* - Your controller sends in a different format, so you need to figure that out, edit the code and recompile. - (A bit advanced) - - +![](preset1.png) +![](preset2.png) diff --git a/btc.jpg b/btc.jpg new file mode 100644 index 0000000..56ace87 Binary files /dev/null and b/btc.jpg differ diff --git a/connect.png b/connect.png new file mode 100644 index 0000000..49d1140 Binary files /dev/null and b/connect.png differ diff --git a/connect_ok.png b/connect_ok.png new file mode 100644 index 0000000..48d6150 Binary files /dev/null and b/connect_ok.png differ diff --git a/control.png b/control.png new file mode 100644 index 0000000..ad7d491 Binary files /dev/null and b/control.png differ diff --git a/in_use.png b/in_use.png deleted file mode 100644 index 5c8774b..0000000 Binary files a/in_use.png and /dev/null differ diff --git a/inc/vjoyinterface.h b/inc/vjoyinterface.h deleted file mode 100644 index f92d8d5..0000000 --- a/inc/vjoyinterface.h +++ /dev/null @@ -1,76 +0,0 @@ -// The following ifdef block is the standard way of creating macros which make exporting -// from a DLL simpler. All files within this DLL are compiled with the VJOYINTERFACE_EXPORTS -// symbol defined on the command line. this symbol should not be defined on any project -// that uses this DLL. This way any other project whose source files include this file see -// VJOYINTERFACE_API functions as being imported from a DLL, whereas this DLL sees symbols -// defined with this macro as being exported. -#ifdef VJOYINTERFACE_EXPORTS -#define VJOYINTERFACE_API __declspec(dllexport) -#else -#define VJOYINTERFACE_API __declspec(dllimport) -#endif - -///////////////////////////// vJoy device (collection) status //////////////////////////////////////////// -#ifndef VJDSTAT -#define VJDSTAT -enum VjdStat /* Declares an enumeration data type called BOOLEAN */ -{ - VJD_STAT_OWN, // The vJoy Device is owned by this application. - VJD_STAT_FREE, // The vJoy Device is NOT owned by any application (including this one). - VJD_STAT_BUSY, // The vJoy Device is owned by another application. It cannot be acquired by this application. - VJD_STAT_MISS, // The vJoy Device is missing. It either does not exist or the driver is down. - VJD_STAT_UNKN // Unknown -}; -#endif -extern "C" { -///////////////////////////// vJoy device (collection) Control interface ///////////////////////////////// -/* - These functions allow writing feeders and other applications that interface with vJoy - It is assumed that only one vJoy top-device (= Raw PDO) exists. - This top-level device can have up to 16 siblings (=top-level Reports/collections) - Each sibling is refered to as a "vJoy Device" and is attributed a unique Report ID (Range: 1-16). - - Naming convetion: - VJD = vJoy Device - rID = Report ID -*/ - -///// General driver data -VJOYINTERFACE_API SHORT __cdecl GetvJoyVersion(void); -VJOYINTERFACE_API BOOL __cdecl vJoyEnabled(void); -VJOYINTERFACE_API PVOID __cdecl GetvJoyProductString(void); -VJOYINTERFACE_API PVOID __cdecl GetvJoyManufacturerString(void); -VJOYINTERFACE_API PVOID __cdecl GetvJoySerialNumberString(void); - - -///// vJoy Device properties -VJOYINTERFACE_API int __cdecl GetVJDButtonNumber(UINT rID); // Get the number of buttons defined in the specified VDJ -VJOYINTERFACE_API int __cdecl GetVJDDiscPovNumber(UINT rID); // Get the number of descrete-type POV hats defined in the specified VDJ -VJOYINTERFACE_API int __cdecl GetVJDContPovNumber(UINT rID); // Get the number of descrete-type POV hats defined in the specified VDJ -VJOYINTERFACE_API BOOL __cdecl GetVJDAxisExist(UINT rID, UINT Axis); // Test if given axis defined in the specified VDJ -VJOYINTERFACE_API BOOL __cdecl GetVJDAxisMax(UINT rID, UINT Axis, LONG * Max); // Get logical Maximum value for a given axis defined in the specified VDJ -VJOYINTERFACE_API BOOL __cdecl GetVJDAxisMin(UINT rID, UINT Axis, LONG * Min); // Get logical Minimum value for a given axis defined in the specified VDJ - -///// Write access to vJoy Device - Basic -VJOYINTERFACE_API BOOL __cdecl AcquireVJD(UINT rID); // Acquire the specified vJoy Device. -VJOYINTERFACE_API VOID __cdecl RelinquishVJD(UINT rID); // Relinquish the specified vJoy Device. -VJOYINTERFACE_API BOOL __cdecl UpdateVJD(UINT rID, PVOID pData); // Update the position data of the specified vJoy Device. -VJOYINTERFACE_API enum VjdStat __cdecl GetVJDStatus(UINT rID); // Get the status of the specified vJoy Device. - -///// Write access to vJoy Device - Modifyiers -// This group of functions modify the current value of the position data -// They replace the need to create a structure of position data then call UpdateVJD - -//// Reset functions -VJOYINTERFACE_API BOOL __cdecl ResetVJD(UINT rID); // Reset all controls to predefined values in the specified VDJ -VJOYINTERFACE_API VOID __cdecl ResetAll(void); // Reset all controls to predefined values in all VDJ -VJOYINTERFACE_API BOOL __cdecl ResetButtons(UINT rID); // Reset all buttons (To 0) in the specified VDJ -VJOYINTERFACE_API BOOL __cdecl ResetPovs(UINT rID); // Reset all POV Switches (To -1) in the specified VDJ - -// Write data -VJOYINTERFACE_API BOOL __cdecl SetAxis(LONG Value, UINT rID, UINT Axis); // Write Value to a given axis defined in the specified VDJ -VJOYINTERFACE_API BOOL __cdecl SetBtn(BOOL Value, UINT rID, UCHAR nBtn); // Write Value to a given button defined in the specified VDJ -VJOYINTERFACE_API BOOL __cdecl SetDiscPov(int Value, UINT rID, UCHAR nPov); // Write Value to a given descrete POV defined in the specified VDJ -VJOYINTERFACE_API BOOL __cdecl SetContPov(DWORD Value, UINT rID, UCHAR nPov); // Write Value to a given continuous POV defined in the specified VDJ - -} // extern "C" \ No newline at end of file diff --git a/lib/amd64/vJoyInterface.dll b/lib/amd64/vJoyInterface.dll deleted file mode 100644 index 5db1ed5..0000000 Binary files a/lib/amd64/vJoyInterface.dll and /dev/null differ diff --git a/lib/amd64/vJoyInterface.lib b/lib/amd64/vJoyInterface.lib deleted file mode 100644 index af63eb4..0000000 Binary files a/lib/amd64/vJoyInterface.lib and /dev/null differ diff --git a/lib/amd64/vJoyInterface.pdb b/lib/amd64/vJoyInterface.pdb deleted file mode 100644 index 1a1550a..0000000 Binary files a/lib/amd64/vJoyInterface.pdb and /dev/null differ diff --git a/lib/vJoyInterface.dll b/lib/vJoyInterface.dll deleted file mode 100644 index f1e7b0a..0000000 Binary files a/lib/vJoyInterface.dll and /dev/null differ diff --git a/lib/vJoyInterface.lib b/lib/vJoyInterface.lib deleted file mode 100644 index 7752ec4..0000000 Binary files a/lib/vJoyInterface.lib and /dev/null differ diff --git a/lib/vJoyInterface.pdb b/lib/vJoyInterface.pdb deleted file mode 100644 index 551de22..0000000 Binary files a/lib/vJoyInterface.pdb and /dev/null differ diff --git a/mDjiController.zip b/mDjiController.zip deleted file mode 100644 index 39cb146..0000000 Binary files a/mDjiController.zip and /dev/null differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..7eaae01 --- /dev/null +++ b/main.py @@ -0,0 +1,255 @@ +import argparse +import struct +import time +from threading import Thread + +import serial.tools.list_ports +import vgamepad as vg + +parser = argparse.ArgumentParser(description='DJI Mavic 3 RC231, RC-N1)') +parser.add_argument('-p', '--port', help='RC Serial Port', default="COM9") + +args = parser.parse_args() +gamepad = vg.VX360Gamepad() +camera = 0 + +events = ( + gamepad.left_trigger, + gamepad.left_joystick(-16384, 16384), + gamepad.right_joystick(-16384, 16384), + ) + +gamepad.reset() +time.sleep(1) + +def calc_checksum(packet, plength): + + crc = [0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78] + + # Seeds + # v = 0x1012 #Naza M + # v = 0x1013 #Phantom 2 + # v = 0x7000 #Naza M V2 + v = 0x3692 #P3/P4/Mavic + + for i in range(0, plength): + vv = v >> 8 + v = vv ^ crc[((packet[i] ^ v) & 0xFF)] + return v + +def calc_pkt55_hdr_checksum(seed, packet, plength): + arr_2A103 = [0x00,0x5E,0xBC,0xE2,0x61,0x3F,0xDD,0x83,0xC2,0x9C,0x7E,0x20,0xA3,0xFD,0x1F,0x41, + 0x9D,0xC3,0x21,0x7F,0xFC,0xA2,0x40,0x1E,0x5F,0x01,0xE3,0xBD,0x3E,0x60,0x82,0xDC, + 0x23,0x7D,0x9F,0xC1,0x42,0x1C,0xFE,0xA0,0xE1,0xBF,0x5D,0x03,0x80,0xDE,0x3C,0x62, + 0xBE,0xE0,0x02,0x5C,0xDF,0x81,0x63,0x3D,0x7C,0x22,0xC0,0x9E,0x1D,0x43,0xA1,0xFF, + 0x46,0x18,0xFA,0xA4,0x27,0x79,0x9B,0xC5,0x84,0xDA,0x38,0x66,0xE5,0xBB,0x59,0x07, + 0xDB,0x85,0x67,0x39,0xBA,0xE4,0x06,0x58,0x19,0x47,0xA5,0xFB,0x78,0x26,0xC4,0x9A, + 0x65,0x3B,0xD9,0x87,0x04,0x5A,0xB8,0xE6,0xA7,0xF9,0x1B,0x45,0xC6,0x98,0x7A,0x24, + 0xF8,0xA6,0x44,0x1A,0x99,0xC7,0x25,0x7B,0x3A,0x64,0x86,0xD8,0x5B,0x05,0xE7,0xB9, + 0x8C,0xD2,0x30,0x6E,0xED,0xB3,0x51,0x0F,0x4E,0x10,0xF2,0xAC,0x2F,0x71,0x93,0xCD, + 0x11,0x4F,0xAD,0xF3,0x70,0x2E,0xCC,0x92,0xD3,0x8D,0x6F,0x31,0xB2,0xEC,0x0E,0x50, + 0xAF,0xF1,0x13,0x4D,0xCE,0x90,0x72,0x2C,0x6D,0x33,0xD1,0x8F,0x0C,0x52,0xB0,0xEE, + 0x32,0x6C,0x8E,0xD0,0x53,0x0D,0xEF,0xB1,0xF0,0xAE,0x4C,0x12,0x91,0xCF,0x2D,0x73, + 0xCA,0x94,0x76,0x28,0xAB,0xF5,0x17,0x49,0x08,0x56,0xB4,0xEA,0x69,0x37,0xD5,0x8B, + 0x57,0x09,0xEB,0xB5,0x36,0x68,0x8A,0xD4,0x95,0xCB,0x29,0x77,0xF4,0xAA,0x48,0x16, + 0xE9,0xB7,0x55,0x0B,0x88,0xD6,0x34,0x6A,0x2B,0x75,0x97,0xC9,0x4A,0x14,0xF6,0xA8, + 0x74,0x2A,0xC8,0x96,0x15,0x4B,0xA9,0xF7,0xB6,0xE8,0x0A,0x54,0xD7,0x89,0x6B,0x35] + + chksum = seed + for i in range(0, plength): + chksum = arr_2A103[((packet[i] ^ chksum) & 0xFF)]; + return chksum + +def send_duml(s, source, target, cmd_type, cmd_set, cmd_id, payload = None): + global sequence_number + sequence_number = 0x34eb + packet = bytearray.fromhex(u'55') + length = 13 + if payload is not None: + length = length + len(payload) + + if length > 0x3ff: + print("Packet too large") + exit(1) + + packet += struct.pack('B', length & 0xff) + packet += struct.pack('B', (length >> 8) | 0x4) # MSB of length and protocol version + hdr_crc = calc_pkt55_hdr_checksum(0x77, packet, 3) + packet += struct.pack('B', hdr_crc) + packet += struct.pack('B', source) + packet += struct.pack('B', target) + packet += struct.pack(' (min 0, center 16384, max 32768) +def parseInput(input, name): + output = (int.from_bytes(input, byteorder='little') - 1024) * 2 * 4096 // 165 + if output >= 32768: + output = 32767 + +# print(output, "test") + + return output + +st = {"rh": 0, "rv": 0, "lh": 0, "lv": 0} + +def threaded_function(): + while(True): + time.sleep(0.1) + #print("working ...") + gamepad.left_joystick(int(st["lh"]), int(st["lv"])) + gamepad.right_joystick(int(st["rh"]), int(st["rv"])) + if camera > 32000: + gamepad.press_button(vg.XUSB_BUTTON.XUSB_GAMEPAD_Y) #restart race + if camera < -32000: + gamepad.press_button(vg.XUSB_BUTTON.XUSB_GAMEPAD_B) #recover drone + if camera == 0: + gamepad.release_button(vg.XUSB_BUTTON.XUSB_GAMEPAD_Y) + gamepad.release_button(vg.XUSB_BUTTON.XUSB_GAMEPAD_B) + gamepad.update() + +thread = Thread(target = threaded_function, args = ()) +thread.start() +#thread.join() + +try: + # enable simulator mode for RC (without this stick positions are sent very slow by RC) + send_duml(s, 0x0a, 0x06, 0x40, 0x06, 0x24, bytearray.fromhex('01')) + + while True: + + #time.sleep(0.1) + # read channel values + send_duml(s, 0x0a, 0x06, 0x40, 0x06, 0x01, bytearray.fromhex('')) + #s.write(bytearray.fromhex('55 0d 04 33 0a 06 eb 34 40 06 01 74 24')) + # Don't write to a new line every time. +# print('\rPinged. ', end='') + + # read duml + buffer = bytearray.fromhex('') + while True: + b = s.read(1) + if b == bytearray.fromhex('55'): + buffer.extend(b) + ph = s.read(2) + buffer.extend(ph) + ph = struct.unpack('> 10 + pc = s.read(1) + buffer.extend(pc) + pd = s.read(pl - 4) + buffer.extend(pd) + break + else: + break + data = buffer +# print(' '.join(format(x, '02x') for x in data)) +# time.sleep(0.5) + + # Reverse-engineered. Controller input seems to always be len 38. + if len(data) == 38: + # Reverse-engineered + st["rh"] = parseInput(data[13:15], 'lv') + st["rv"] = parseInput(data[16:18], 'lh') + + st["lv"] = parseInput(data[19:21], 'rv') + st["lh"] = parseInput(data[22:24], 'rh') + + camera = parseInput(data[25:27], 'cam') + + #print(data) + #with uinput.Device(events) as device: + #time.sleep(1) + #else: + # print(len(data)) + + # Log to console. + #print('L: H{0:06d},V{1:06d}; R: H{2:06d},V{3:06d}, CAM: {4:06d}\n'.format(left_horizontal, left_vertical, right_horizontal, right_vertical, camera), end='') +except serial.SerialException as e: + # Stylistic: Newline to stop data update and spacing. + print('\n\nCould not read/write:', e) +except KeyboardInterrupt: + # Stylistic: Newline to stop data update and spacing. + print('\n\nDetected keyboard interrupt.') + + pass + +print('Stopping.') diff --git a/monoQR.jpg b/monoQR.jpg new file mode 100644 index 0000000..6f37012 Binary files /dev/null and b/monoQR.jpg differ diff --git a/preset1.png b/preset1.png new file mode 100644 index 0000000..b43a380 Binary files /dev/null and b/preset1.png differ diff --git a/preset2.png b/preset2.png new file mode 100644 index 0000000..2b68657 Binary files /dev/null and b/preset2.png differ diff --git a/src/Joystick.cpp b/src/Joystick.cpp deleted file mode 100644 index 659cf1a..0000000 --- a/src/Joystick.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "Joystick.h" -#include "vjoyinterface.h" - -Joystick::Joystick(int id, int logging) -{ - interfaceId = id; - if (logging == 0) { - log = true; - } - - printf("Creating vJoy connection\n"); - - if (!vJoyEnabled()) { - printf("vJoy driver not enabled: Failed Getting vJoy attributes.\n"); - return; - } else { - printf("Driver found:\nVendor:%S, Product:%S, Ver:%S\n", TEXT(GetvJoyManufacturerString()), TEXT(GetvJoyProductString()), TEXT(GetvJoySerialNumberString())); - }; - - VjdStat status = GetVJDStatus(interfaceId); - switch (status) - { - case VJD_STAT_OWN: - printf("vJoy Device %d is already owned by this feeder\n", interfaceId); - break; - case VJD_STAT_FREE: - printf("vJoy Device %d is free\n", interfaceId); - break; - case VJD_STAT_BUSY: - printf("vJoy Device %d is already owned by another feeder\nCannot continue\n", interfaceId); - return; - case VJD_STAT_MISS: - printf("vJoy Device %d is not installed or disabled\nCannot continue\n", interfaceId); - return; - default: - printf("vJoy Device %d general error\nCannot continue\n", interfaceId); - return; - }; - - if (!AcquireVJD(interfaceId)) { - printf("Failed to acquire vJoy Interface number %d\n", interfaceId); - return; - } - else { - ResetVJD(interfaceId); - connected = true; - printf("Acquired vJoy Interface number %d\n", interfaceId); - } -} - -void Joystick::update(int l_hor, int l_ver, int r_hor, int r_ver, int lever_left, int lever_right, int camera) -{ - // Values must be between 0x0 and 0x8000 - // The values from the controller are between -1000 and 1000, so we just scale them between 0 and 16000 (maximum range in vJoyMonitor) - l_hor += 1000; - l_ver += 1000; - r_hor += 1000; - r_ver += 1000; - camera -= 1000; - - l_hor *= 16; - l_ver *= 16; - r_hor *= 16; - r_ver *= 16; - camera *= -16; // negative multiplication to make the value feel natural (left = negative | right = positive) - - // Read values from left lever - // -780 = 'OFF' position | 0 = 'CL' position | 780 = 'HL' position - if (lever_left == -780) { - button_1 = true; - button_2 = false; - button_3 = false; - } - else if (lever_left == 780) { - button_1 = false; - button_2 = false; - button_3 = true; - } // else remain as before, sometimes this value is a bit weird - else if (lever_left == 0) { - button_1 = false; - button_2 = true; - button_3 = false; - } - - // Read values from right lever - // -780 = 'GPS' position | 0 = 'ATTI' position | 780 = 'ATTI' position - if (lever_right == -780) { - button_4 = true; - button_5 = false; - button_6 = false; - } - else if (lever_right == 780) { - button_4 = false; - button_5 = false; - button_6 = true; - } // else remain as before, sometimes this value is a bit weird - else { - button_4 = false; - button_5 = true; - button_6 = false; - } - - if (log) - printf("L vert: %-5d | L hori: %-5d | R vert: %-5d | R hori: %-5d | btn1: %-d | btn2: %-d | btn3: %-d | btn4: %-d | btn5: %-d | btn6: %-d | camera: %-d\n", l_ver, l_hor, r_ver, r_hor, button_1, button_2, button_3, button_4, button_5, button_6, camera); - - // Send stick values to vJoy - SetAxis(l_hor, interfaceId, HID_USAGE_X); - SetAxis(l_ver, interfaceId, HID_USAGE_Y); - SetAxis(r_hor, interfaceId, HID_USAGE_Z); - SetAxis(r_ver, interfaceId, HID_USAGE_RX); - SetAxis(camera, interfaceId, HID_USAGE_RY); - - // Send button values to vJoy - SetBtn(button_1, interfaceId, 1); - SetBtn(button_2, interfaceId, 2); - SetBtn(button_3, interfaceId, 3); - SetBtn(button_4, interfaceId, 4); - SetBtn(button_5, interfaceId, 5); - SetBtn(button_6, interfaceId, 6); -} - -Joystick::~Joystick() -{ - RelinquishVJD(interfaceId); - printf("Disconnected from joystick\n"); -} \ No newline at end of file diff --git a/src/Joystick.h b/src/Joystick.h deleted file mode 100644 index fbcaa5e..0000000 --- a/src/Joystick.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -#define HID_USAGE_X 0x30 -#define HID_USAGE_Y 0x31 -#define HID_USAGE_Z 0x32 -#define HID_USAGE_RX 0x33 -#define HID_USAGE_RY 0x34 -#define HID_USAGE_RZ 0x35 - -class Joystick -{ -private: - bool button_1, button_2, button_3, button_4, button_5, button_6; - bool connected; - unsigned int interfaceId; - bool log = false; -public: - Joystick(int interfaceId, int logging); - ~Joystick(); - void update(int l_hor, int l_ver, int r_hor, int r_ver, int toggle_left, int toggle_right, int camera); - bool isConnected() { return connected; } -}; \ No newline at end of file diff --git a/src/Main.cpp b/src/Main.cpp deleted file mode 100644 index faca1e5..0000000 --- a/src/Main.cpp +++ /dev/null @@ -1,125 +0,0 @@ - -#include -#include -#include -#include - -#include "Serial.h" -#include "Joystick.h" - - - -short littleEndiansToShort(int first, int second) { - if (first < 0) { - first = 256 + first; - } - - short combined = second << 8 | first; - return combined; - -} - -void run(char* portName, int stickId, int logging) { - - char initData[] = { 0x55, 0xaa, 0x55, 0xaa, 0x1e, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x00, 0x04, 0x04, 0x74, 0x94, 0x35, 0x00, 0xd8, 0xc0, 0x41, 0x00, 0x30, 0xf6, 0x08, 0x00, 0x00, 0xf6, 0x69, 0x9c, 0x01, 0xe8}; - char pingData[] = { 0x55, 0xaa, 0x55, 0xaa, 0x1e, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x02, 0x00, 0x80, 0x00, 0x06, 0x01, 0x28, 0x97, 0xae, 0x03, 0x28, 0x36, 0xa4, 0x03, 0x28, 0x36, 0xa4, 0x03, 0xab, 0xa7, 0x30, 0x00, 0x03, 0x53}; - - char incomingData[256] = ""; - int dataLength = 256; - int readResult = 0; - bool shouldRun = true; - - - Joystick j(stickId, logging); - if (!j.isConnected()) { - printf("Couldn't connect to vJoy, quitting...\n"); - return; - } - printf("\n"); - - Serial s(portName); - if (!s.IsConnected()) { - printf("Couldn't connect to COM port, quitting...\n"); - return; - } - - printf("\nEverything is ready\n\n"); - s.WriteData(initData, 34); - - printf("Running...\nPress ESC to quit\n"); - Sleep(2000); - - while (s.IsConnected() && shouldRun) - { - s.WriteData(pingData, 34); // write this once in a while, otherwise it stops sending? :O - readResult = s.ReadData(incomingData, dataLength); - - if (readResult == 76 && incomingData[0] == 0x55) { // probably positioning data - short left_vertical = littleEndiansToShort(incomingData[39], incomingData[40]); - short left_horizontal = littleEndiansToShort(incomingData[43], incomingData[44]); - - short right_horizontal = littleEndiansToShort(incomingData[31], incomingData[32]); - short right_vertical = littleEndiansToShort(incomingData[35], incomingData[36]); - - short left_lever = littleEndiansToShort(incomingData[47], incomingData[48]); - short right_lever = littleEndiansToShort(incomingData[51], incomingData[52]); - - short camera = littleEndiansToShort(incomingData[55], incomingData[56]); - - // update our virtual joystick - j.update(left_horizontal, left_vertical, right_horizontal, right_vertical, left_lever, right_lever, camera); - } - - - if (GetAsyncKeyState(VK_ESCAPE)) { - shouldRun = false; - printf("\n\nDetected ESC, quitting...\n"); - } - - Sleep(10); - } - -} - -int main() { - std::string in; - int portNr, stickId, logging; - - - printf("Select port number (default COM \"3\"): "); - std::getline(std::cin, in); - portNr = atoi(in.c_str()); - - if (portNr < 1) { - portNr = 3; - } - - char port[100]; - sprintf_s(port, "COM%d", portNr); - - - printf("Select vJoy configuration (default \"1\"): "); - std::getline(std::cin, in); - stickId = atoi(in.c_str()); - - if (stickId < 1) { - stickId = 1; - } - - - printf("Disable logging? 1 for off (default \"0\"): "); - std::getline(std::cin, in); - logging = atoi(in.c_str()); - - if (logging != 0) { - logging = 1; - } - - printf("Starting...\n\n"); - run(port, stickId, logging); - printf("Closing down...\n\n"); - - - system("pause"); - return 0; -} \ No newline at end of file diff --git a/src/Serial.cpp b/src/Serial.cpp deleted file mode 100644 index bdf3b02..0000000 --- a/src/Serial.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "Serial.h" - -/* - * Adopted from http://playground.arduino.cc/Interfacing/CPPWindows - */ - -Serial::Serial(char *portName) -{ - printf("Creating serial connection\n"); - //We're not yet connected - this->connected = false; - - //Try to connect to the given port through CreateFile - this->hSerial = CreateFile(portName, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - - //Check if the connection was successful - if (this->hSerial == INVALID_HANDLE_VALUE) - { - //If not success full display an Error - if (GetLastError() == ERROR_FILE_NOT_FOUND){ - - //Print Error if necessary - printf("ERROR: Handle was not attached. Reason: %s not available.\n", portName); - - } - else - { - printf("Couldn't connect to COM port, unknown why..\n"); - } - } - else - { - printf("Found serial port\n"); - //If connected we try to set the comm parameters - DCB dcbSerialParams = { 0 }; - - //Try to get the current - if (!GetCommState(this->hSerial, &dcbSerialParams)) - { - //If impossible, show an error - printf("failed to get current serial parameters!\n"); - } - else - { - //Define serial connection parameters for the Arduino board - dcbSerialParams.BaudRate = 115200; - dcbSerialParams.ByteSize = 8; - dcbSerialParams.StopBits = ONESTOPBIT; - dcbSerialParams.Parity = NOPARITY; - - //Set the parameters and check for their proper application - if (!SetCommState(hSerial, &dcbSerialParams)) - { - printf("ALERT: Could not set Serial Port parameters\n"); - } - else - { - printf("Connected to serial port\n"); - //If everything went fine we're connected - this->connected = true; - //Sleep(200); - } - } - } - -} - -Serial::~Serial() -{ - //Check if we are connected before trying to disconnect - if (this->connected) - { - //We're no longer connected - this->connected = false; - //Close the serial handler - CloseHandle(this->hSerial); - printf("Closed serial connection\n"); - } -} - -int Serial::ReadData(char *buffer, unsigned int nbChar) -{ - //Number of bytes we'll have read - DWORD bytesRead; - //Number of bytes we'll really ask to read - unsigned int toRead; - - //Use the ClearCommError function to get status info on the Serial port - ClearCommError(this->hSerial, &this->errors, &this->status); - - //Check if there is something to read - if (this->status.cbInQue>0) - { - //If there is we check if there is enough data to read the required number - //of characters, if not we'll read only the available characters to prevent - //locking of the application. - if (this->status.cbInQue>nbChar) - { - toRead = nbChar; - } - else - { - toRead = this->status.cbInQue; - } - - //Try to read the require number of chars, and return the number of read bytes on success - if (ReadFile(this->hSerial, buffer, toRead, &bytesRead, NULL) && bytesRead != 0) - { - return bytesRead; - } - - } - - //If nothing has been read, or that an error was detected return -1 - return -1; - -} - - -bool Serial::WriteData(char *buffer, unsigned int nbChar) -{ - DWORD bytesSend; - - //Try to write the buffer on the Serial port - if (!WriteFile(this->hSerial, (void *)buffer, nbChar, &bytesSend, 0)) - { - //In case it don't work get comm error and return false - ClearCommError(this->hSerial, &this->errors, &this->status); - - return false; - } - else - return true; -} - -bool Serial::IsConnected() -{ - //Simply return the connection status - return this->connected; -} \ No newline at end of file diff --git a/src/Serial.h b/src/Serial.h deleted file mode 100644 index 6f29ea7..0000000 --- a/src/Serial.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include -#include - -/* -* Adopted from http://playground.arduino.cc/Interfacing/CPPWindows -*/ - -class Serial -{ -private: - HANDLE hSerial; - bool connected; - COMSTAT status; - DWORD errors; - -public: - Serial(char *portName); - ~Serial(); - int ReadData(char *buffer, unsigned int nbChar); - bool WriteData(char *buffer, unsigned int nbChar); - bool IsConnected(); -}; diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..7a84f97 --- /dev/null +++ b/start.bat @@ -0,0 +1 @@ +python main.py \ No newline at end of file